/*!
 * UEditor
 * version: ueditor
 * build: Wed Aug 10 2016 11:06:02 GMT+0800 (CST)
 */
;(function() {
  UEDITOR_CONFIG = window.UEDITOR_CONFIG || {}
  var baidu = window.baidu || {}
  window.baidu = baidu
  window.UE = baidu.editor = window.UE || {}
  UE.plugins = {}
  UE.commands = {}
  UE.instants = {}
  UE.I18N = {}
  UE._customizeUI = {}
  UE.version = '1.4.3'
  var dom = (UE.dom = {})
  var browser = (UE.browser = (function() {
    var agent = navigator.userAgent.toLowerCase()
    var opera = window.opera
    var browser = {
      ie: /(msie\s|trident.*rv:)([\w.]+)/.test(agent),
      opera: !!opera && opera.version,
      webkit: agent.indexOf(' applewebkit/') > -1,
      mac: agent.indexOf('macintosh') > -1,
      quirks: document.compatMode == 'BackCompat'
    }
    browser.gecko = navigator.product == 'Gecko' && !browser.webkit && !browser.opera && !browser.ie
    var version = 0
    if (browser.ie) {
      var v1 = agent.match(/(?:msie\s([\w.]+))/)
      var v2 = agent.match(/(?:trident.*rv:([\w.]+))/)
      if (v1 && v2 && v1[1] && v2[1]) {
        version = Math.max(v1[1] * 1, v2[1] * 1)
      } else {
        if (v1 && v1[1]) {
          version = v1[1] * 1
        } else {
          if (v2 && v2[1]) {
            version = v2[1] * 1
          } else {
            version = 0
          }
        }
      }
      browser.ie11Compat = document.documentMode == 11
      browser.ie9Compat = document.documentMode == 9
      browser.ie8 = !!document.documentMode
      browser.ie8Compat = document.documentMode == 8
      browser.ie7Compat = (version == 7 && !document.documentMode) || document.documentMode == 7
      browser.ie6Compat = version < 7 || browser.quirks
      browser.ie9above = version > 8
      browser.ie9below = version < 9
      browser.ie11above = version > 10
      browser.ie11below = version < 11
    }
    if (browser.gecko) {
      var geckoRelease = agent.match(/rv:([\d\.]+)/)
      if (geckoRelease) {
        geckoRelease = geckoRelease[1].split('.')
        version = geckoRelease[0] * 10000 + (geckoRelease[1] || 0) * 100 + (geckoRelease[2] || 0) * 1
      }
    }
    if (/chrome\/(\d+\.\d)/i.test(agent)) {
      browser.chrome = +RegExp['\x241']
    }
    if (/(\d+\.\d)?(?:\.\d)?\s+safari\/?(\d+\.\d+)?/i.test(agent) && !/chrome/i.test(agent)) {
      browser.safari = +(RegExp['\x241'] || RegExp['\x242'])
    }
    if (browser.opera) {
      version = parseFloat(opera.version())
    }
    if (browser.webkit) {
      version = parseFloat(agent.match(/ applewebkit\/(\d+)/)[1])
    }
    browser.version = version
    browser.isCompatible =
      !browser.mobile &&
      ((browser.ie && version >= 6) ||
        (browser.gecko && version >= 10801) ||
        (browser.opera && version >= 9.5) ||
        (browser.air && version >= 1) ||
        (browser.webkit && version >= 522) ||
        false)
    return browser
  })())
  var ie = browser.ie
  var webkit = browser.webkit
  var gecko = browser.gecko
  var opera = browser.opera
  var utils = (UE.utils = {
    each: function(obj, iterator, context) {
      if (obj == null) {
        return
      }
      if (obj.length === +obj.length) {
        for (var i = 0, l = obj.length; i < l; i++) {
          if (iterator.call(context, obj[i], i, obj) === false) {
            return false
          }
        }
      } else {
        for (var key in obj) {
          if (obj.hasOwnProperty(key)) {
            if (iterator.call(context, obj[key], key, obj) === false) {
              return false
            }
          }
        }
      }
    },
    makeInstance: function(obj) {
      var noop = new Function()
      noop.prototype = obj
      obj = new noop()
      noop.prototype = null
      return obj
    },
    extend: function(t, s, b) {
      if (s) {
        for (var k in s) {
          if (!b || !t.hasOwnProperty(k)) {
            t[k] = s[k]
          }
        }
      }
      return t
    },
    extend2: function(t) {
      var a = arguments
      for (var i = 1; i < a.length; i++) {
        var x = a[i]
        for (var k in x) {
          if (!t.hasOwnProperty(k)) {
            t[k] = x[k]
          }
        }
      }
      return t
    },
    inherits: function(subClass, superClass) {
      var oldP = subClass.prototype
      var newP = utils.makeInstance(superClass.prototype)
      utils.extend(newP, oldP, true)
      subClass.prototype = newP
      return (newP.constructor = subClass)
    },
    bind: function(fn, context) {
      return function() {
        return fn.apply(context, arguments)
      }
    },
    defer: function(fn, delay, exclusion) {
      var timerID
      return function() {
        if (exclusion) {
          clearTimeout(timerID)
        }
        timerID = setTimeout(fn, delay)
      }
    },
    indexOf: function(array, item, start) {
      var index = -1
      start = this.isNumber(start) ? start : 0
      this.each(array, function(v, i) {
        if (i >= start && v === item) {
          index = i
          return false
        }
      })
      return index
    },
    removeItem: function(array, item) {
      for (var i = 0, l = array.length; i < l; i++) {
        if (array[i] === item) {
          array.splice(i, 1)
          i--
        }
      }
    },
    trim: function(str) {
      return str.replace(/(^[ \t\n\r]+)|([ \t\n\r]+$)/g, '')
    },
    listToMap: function(list) {
      if (!list) {
        return {}
      }
      list = utils.isArray(list) ? list : list.split(',')
      for (var i = 0, ci, obj = {}; (ci = list[i++]);) {
        obj[ci.toUpperCase()] = obj[ci] = 1
      }
      return obj
    },
    unhtml: function(str, reg) {
      return str
        ? str.replace(reg || /[&<">'](?:(amp|lt|quot|gt|#39|nbsp|#\d+);)?/g, function(a, b) {
          if (b) {
            return a
          } else {
            return { '<': '&lt;', '&': '&amp;', '"': '&quot;', '>': '&gt;', '\'': '&#39;' }[a]
          }
        })
        : ''
    },
    unhtmlForUrl: function(str, reg) {
      return str
        ? str.replace(reg || /[<">']/g, function(a) {
          return { '<': '&lt;', '&': '&amp;', '"': '&quot;', '>': '&gt;', '\'': '&#39;' }[a]
        })
        : ''
    },
    html: function(str) {
      return str
        ? str.replace(/&((g|l|quo)t|amp|#39|nbsp);/g, function(m) {
          return { '&lt;': '<', '&amp;': '&', '&quot;': '"', '&gt;': '>', '&#39;': '\'', '&nbsp;': ' ' }[m]
        })
        : ''
    },
    cssStyleToDomStyle: (function() {
      var test = document.createElement('div').style
      var cache = {
        float: test.cssFloat != undefined ? 'cssFloat' : test.styleFloat != undefined ? 'styleFloat' : 'float'
      }
      return function(cssName) {
        return (
          cache[cssName] ||
          (cache[cssName] = cssName.toLowerCase().replace(/-./g, function(match) {
            return match.charAt(1).toUpperCase()
          }))
        )
      }
    })(),
    loadFile: (function() {
      var tmpList = []

      function getItem(doc, obj) {
        try {
          for (var i = 0, ci; (ci = tmpList[i++]);) {
            if (ci.doc === doc && ci.url == (obj.src || obj.href)) {
              return ci
            }
          }
        } catch (e) {
          return null
        }
      }

      return function(doc, obj, fn) {
        var item = getItem(doc, obj)
        if (item) {
          if (item.ready) {
            fn && fn()
          } else {
            item.funs.push(fn)
          }
          return
        }
        tmpList.push({ doc: doc, url: obj.src || obj.href, funs: [fn] })
        if (!doc.body) {
          var html = []
          for (var p in obj) {
            if (p == 'tag') {
              continue
            }
            html.push(p + '="' + obj[p] + '"')
          }
          doc.write('<' + obj.tag + ' ' + html.join(' ') + ' ></' + obj.tag + '>')
          return
        }
        if (obj.id && doc.getElementById(obj.id)) {
          return
        }
        var element = doc.createElement(obj.tag)
        delete obj.tag
        for (var p in obj) {
          element.setAttribute(p, obj[p])
        }
        element.onload = element.onreadystatechange = function() {
          if (!this.readyState || /loaded|complete/.test(this.readyState)) {
            item = getItem(doc, obj)
            if (item.funs.length > 0) {
              item.ready = 1
              for (var fi; (fi = item.funs.pop());) {
                fi()
              }
            }
            element.onload = element.onreadystatechange = null
          }
        }
        element.onerror = function() {
          throw Error('The load ' + (obj.href || obj.src) + ' fails,check the url settings of file ueditor.config.js ')
        }
        doc.getElementsByTagName('head')[0].appendChild(element)
      }
    })(),
    isEmptyObject: function(obj) {
      if (obj == null) {
        return true
      }
      if (this.isArray(obj) || this.isString(obj)) {
        return obj.length === 0
      }
      for (var key in obj) {
        if (obj.hasOwnProperty(key)) {
          return false
        }
      }
      return true
    },
    fixColor: function(name, value) {
      if (/color/i.test(name) && /rgba?/.test(value)) {
        var array = value.split(',')
        if (array.length > 3) {
          return ''
        }
        value = '#'
        for (var i = 0, color; (color = array[i++]);) {
          color = parseInt(color.replace(/[^\d]/gi, ''), 10).toString(16)
          value += color.length == 1 ? '0' + color : color
        }
        value = value.toUpperCase()
      }
      return value
    },
    optCss: function(val) {
      var padding, margin, border
      val = val.replace(/(padding|margin|border)\-([^:]+):([^;]+);?/gi, function(str, key, name, val) {
        if (val.split(' ').length == 1) {
          switch (key) {
            case 'padding':
              !padding && (padding = {})
              padding[name] = val
              return ''
            case 'margin':
              !margin && (margin = {})
              margin[name] = val
              return ''
            case 'border':
              return val == 'initial' ? '' : str
          }
        }
        return str
      })

      function opt(obj, name) {
        if (!obj) {
          return ''
        }
        var t = obj.top
        var b = obj.bottom
        var l = obj.left
        var r = obj.right
        var val = ''
        if (!t || !l || !b || !r) {
          for (var p in obj) {
            val += ';' + name + '-' + p + ':' + obj[p] + ';'
          }
        } else {
          val +=
            ';' +
            name +
            ':' +
            (t == b && b == l && l == r
              ? t
              : t == b && l == r
                ? t + ' ' + l
                : l == r
                  ? t + ' ' + l + ' ' + b
                  : t + ' ' + r + ' ' + b + ' ' + l) +
            ';'
        }
        return val
      }

      val += opt(padding, 'padding') + opt(margin, 'margin')
      return val
        .replace(/^[ \n\r\t;]*|[ \n\r\t]*$/, '')
        .replace(/;([ \n\r\t]+)|\1;/g, ';')
        .replace(/(&((l|g)t|quot|#39))?;{2,}/g, function(a, b) {
          return b ? b + ';;' : ';'
        })
    },
    clone: function(source, target) {
      var tmp
      target = target || {}
      for (var i in source) {
        if (source.hasOwnProperty(i)) {
          tmp = source[i]
          if (typeof tmp === 'object') {
            target[i] = utils.isArray(tmp) ? [] : {}
            utils.clone(source[i], target[i])
          } else {
            target[i] = tmp
          }
        }
      }
      return target
    },
    transUnitToPx: function(val) {
      if (!/(pt|cm)/.test(val)) {
        return val
      }
      var unit
      val.replace(/([\d.]+)(\w+)/, function(str, v, u) {
        val = v
        unit = u
      })
      switch (unit) {
        case 'cm':
          val = parseFloat(val) * 25
          break
        case 'pt':
          val = Math.round((parseFloat(val) * 96) / 72)
      }
      return val + (val ? 'px' : '')
    },
    domReady: (function() {
      var fnArr = []

      function doReady(doc) {
        doc.isReady = true
        for (var ci; (ci = fnArr.pop()); ci()) {
        }
      }

      return function(onready, win) {
        win = win || window
        var doc = win.document
        onready && fnArr.push(onready)
        if (doc.readyState === 'complete') {
          doReady(doc)
        } else {
          doc.isReady && doReady(doc)
          if (browser.ie && browser.version != 11) {
            ;(function() {
              if (doc.isReady) {
                return
              }
              try {
                doc.documentElement.doScroll('left')
              } catch (error) {
                setTimeout(arguments.callee, 0)
                return
              }
              doReady(doc)
            })()
            win.attachEvent('onload', function() {
              doReady(doc)
            })
          } else {
            doc.addEventListener(
              'DOMContentLoaded',
              function() {
                doc.removeEventListener('DOMContentLoaded', arguments.callee, false)
                doReady(doc)
              },
              false
            )
            win.addEventListener(
              'load',
              function() {
                doReady(doc)
              },
              false
            )
          }
        }
      }
    })(),
    cssRule:
      browser.ie && browser.version != 11
        ? function(key, style, doc) {
          var indexList, index
          if (style === undefined || (style && style.nodeType && style.nodeType == 9)) {
            doc = style && style.nodeType && style.nodeType == 9 ? style : doc || document
            indexList = doc.indexList || (doc.indexList = {})
            index = indexList[key]
            if (index !== undefined) {
              return doc.styleSheets[index].cssText
            }
            return undefined
          }
          doc = doc || document
          indexList = doc.indexList || (doc.indexList = {})
          index = indexList[key]
          if (style === '') {
            if (index !== undefined) {
              doc.styleSheets[index].cssText = ''
              delete indexList[key]
              return true
            }
            return false
          }
          if (index !== undefined) {
            sheetStyle = doc.styleSheets[index]
          } else {
            sheetStyle = doc.createStyleSheet('', (index = doc.styleSheets.length))
            indexList[key] = index
          }
          sheetStyle.cssText = style
        }
        : function(key, style, doc) {
          var head, node
          if (style === undefined || (style && style.nodeType && style.nodeType == 9)) {
            doc = style && style.nodeType && style.nodeType == 9 ? style : doc || document
            node = doc.getElementById(key)
            return node ? node.innerHTML : undefined
          }
          doc = doc || document
          node = doc.getElementById(key)
          if (style === '') {
            if (node) {
              node.parentNode.removeChild(node)
              return true
            }
            return false
          }
          if (node) {
            node.innerHTML = style
          } else {
            node = doc.createElement('style')
            node.id = key
            node.innerHTML = style
            doc.getElementsByTagName('head')[0].appendChild(node)
          }
        },
    sort: function(array, compareFn) {
      compareFn =
        compareFn ||
        function(item1, item2) {
          return item1.localeCompare(item2)
        }
      for (var i = 0, len = array.length; i < len; i++) {
        for (var j = i, length = array.length; j < length; j++) {
          if (compareFn(array[i], array[j]) > 0) {
            var t = array[i]
            array[i] = array[j]
            array[j] = t
          }
        }
      }
      return array
    },
    serializeParam: function(json) {
      var strArr = []
      for (var i in json) {
        if (i == 'method' || i == 'timeout' || i == 'async') {
          continue
        }
        if (!((typeof json[i]).toLowerCase() == 'function' || (typeof json[i]).toLowerCase() == 'object')) {
          strArr.push(encodeURIComponent(i) + '=' + encodeURIComponent(json[i]))
        } else {
          if (utils.isArray(json[i])) {
            for (var j = 0; j < json[i].length; j++) {
              strArr.push(encodeURIComponent(i) + '[]=' + encodeURIComponent(json[i][j]))
            }
          }
        }
      }
      return strArr.join('&')
    },
    formatUrl: function(url) {
      var u = url.replace(/&&/g, '&')
      u = u.replace(/\?&/g, '?')
      u = u.replace(/&$/g, '')
      u = u.replace(/&#/g, '#')
      u = u.replace(/&+/g, '&')
      return u
    },
    isCrossDomainUrl: function(url) {
      var a = document.createElement('a')
      a.href = url
      if (browser.ie) {
        a.href = a.href
      }
      return !(
        a.protocol == location.protocol &&
        a.hostname == location.hostname &&
        (a.port == location.port || (a.port == '80' && location.port == '') || (a.port == '' && location.port == '80'))
      )
    },
    clearEmptyAttrs: function(obj) {
      for (var p in obj) {
        if (obj[p] === '') {
          delete obj[p]
        }
      }
      return obj
    },
    str2json: function(s) {
      if (!utils.isString(s)) {
        return null
      }
      if (window.JSON) {
        return JSON.parse(s)
      } else {
        return new Function('return ' + utils.trim(s || ''))()
      }
    },
    json2str: (function() {
      if (window.JSON) {
        return JSON.stringify
      } else {
        var escapeMap = { '\b': '\\b', '\t': '\\t', '\n': '\\n', '\f': '\\f', '\r': '\\r', '"': '\\"', '\\': '\\\\' }

        function encodeString(source) {
          if (/["\\\x00-\x1f]/.test(source)) {
            source = source.replace(/["\\\x00-\x1f]/g, function(match) {
              var c = escapeMap[match]
              if (c) {
                return c
              }
              c = match.charCodeAt()
              return '\\u00' + Math.floor(c / 16).toString(16) + (c % 16).toString(16)
            })
          }
          return '"' + source + '"'
        }

        function encodeArray(source) {
          var result = ['[']
          var l = source.length
          var preComma
          var i
          var item
          for (i = 0; i < l; i++) {
            item = source[i]
            switch (typeof item) {
              case 'undefined':
              case 'function':
              case 'unknown':
                break
              default:
                if (preComma) {
                  result.push(',')
                }
                result.push(utils.json2str(item))
                preComma = 1
            }
          }
          result.push(']')
          return result.join('')
        }

        function pad(source) {
          return source < 10 ? '0' + source : source
        }

        function encodeDate(source) {
          return (
            '"' +
            source.getFullYear() +
            '-' +
            pad(source.getMonth() + 1) +
            '-' +
            pad(source.getDate()) +
            'T' +
            pad(source.getHours()) +
            ':' +
            pad(source.getMinutes()) +
            ':' +
            pad(source.getSeconds()) +
            '"'
          )
        }

        return function(value) {
          switch (typeof value) {
            case 'undefined':
              return 'undefined'
            case 'number':
              return isFinite(value) ? String(value) : 'null'
            case 'string':
              return encodeString(value)
            case 'boolean':
              return String(value)
            default:
              if (value === null) {
                return 'null'
              } else {
                if (utils.isArray(value)) {
                  return encodeArray(value)
                } else {
                  if (utils.isDate(value)) {
                    return encodeDate(value)
                  } else {
                    var result = ['{']
                    var encode = utils.json2str
                    var preComma
                    var item
                    for (var key in value) {
                      if (Object.prototype.hasOwnProperty.call(value, key)) {
                        item = value[key]
                        switch (typeof item) {
                          case 'undefined':
                          case 'unknown':
                          case 'function':
                            break
                          default:
                            if (preComma) {
                              result.push(',')
                            }
                            preComma = 1
                            result.push(encode(key) + ':' + encode(item))
                        }
                      }
                    }
                    result.push('}')
                    return result.join('')
                  }
                }
              }
          }
        }
      }
    })()
  })
  utils.each(['String', 'Function', 'Array', 'Number', 'RegExp', 'Object', 'Date'], function(v) {
    UE.utils['is' + v] = function(obj) {
      return Object.prototype.toString.apply(obj) == '[object ' + v + ']'
    }
  })
  var EventBase = (UE.EventBase = function() {
  })
  EventBase.prototype = {
    addListener: function(types, listener) {
      types = utils.trim(types).split(/\s+/)
      for (var i = 0, ti; (ti = types[i++]);) {
        getListener(this, ti, true).push(listener)
      }
    },
    on: function(types, listener) {
      return this.addListener(types, listener)
    },
    off: function(types, listener) {
      return this.removeListener(types, listener)
    },
    trigger: function() {
      return this.fireEvent.apply(this, arguments)
    },
    removeListener: function(types, listener) {
      types = utils.trim(types).split(/\s+/)
      for (var i = 0, ti; (ti = types[i++]);) {
        utils.removeItem(getListener(this, ti) || [], listener)
      }
    },
    fireEvent: function() {
      var types = arguments[0]
      types = utils.trim(types).split(' ')
      for (var i = 0, ti; (ti = types[i++]);) {
        var listeners = getListener(this, ti)
        var r
        var t
        var k
        if (listeners) {
          k = listeners.length
          while (k--) {
            if (!listeners[k]) {
              continue
            }
            t = listeners[k].apply(this, arguments)
            if (t === true) {
              return t
            }
            if (t !== undefined) {
              r = t
            }
          }
        }
        if ((t = this['on' + ti.toLowerCase()])) {
          r = t.apply(this, arguments)
        }
      }
      return r
    }
  }

  function getListener(obj, type, force) {
    var allListeners
    type = type.toLowerCase()
    return (
      (allListeners = obj.__allListeners || (force && (obj.__allListeners = {}))) &&
      (allListeners[type] || (force && (allListeners[type] = [])))
    )
  }

  var dtd = (dom.dtd = (function() {
    function _(s) {
      for (var k in s) {
        s[k.toUpperCase()] = s[k]
      }
      return s
    }

    var X = utils.extend2
    var A = _({ isindex: 1, fieldset: 1 })
    var B = _({ input: 1, button: 1, select: 1, textarea: 1, label: 1 })
    var C = X(_({ a: 1 }), B)
    var D = X({ iframe: 1 }, C)
    var E = _({
      hr: 1,
      ul: 1,
      menu: 1,
      div: 1,
      blockquote: 1,
      noscript: 1,
      table: 1,
      center: 1,
      address: 1,
      dir: 1,
      pre: 1,
      h5: 1,
      dl: 1,
      h4: 1,
      noframes: 1,
      h6: 1,
      ol: 1,
      h1: 1,
      h3: 1,
      h2: 1
    })
    var F = _({ ins: 1, del: 1, script: 1, style: 1 })
    var G = X(
      _({
        b: 1,
        acronym: 1,
        bdo: 1,
        var: 1,
        '#': 1,
        abbr: 1,
        code: 1,
        br: 1,
        i: 1,
        cite: 1,
        kbd: 1,
        u: 1,
        strike: 1,
        s: 1,
        tt: 1,
        strong: 1,
        q: 1,
        samp: 1,
        em: 1,
        dfn: 1,
        span: 1
      }),
      F
    )
    var H = X(
      _({ sub: 1, img: 1, embed: 1, object: 1, sup: 1, basefont: 1, map: 1, applet: 1, font: 1, big: 1, small: 1 }),
      G
    )
    var I = X(_({ p: 1 }), H)
    var J = X(_({ iframe: 1 }), H, B)
    var K = _({
      img: 1,
      embed: 1,
      noscript: 1,
      br: 1,
      kbd: 1,
      center: 1,
      button: 1,
      basefont: 1,
      h5: 1,
      h4: 1,
      samp: 1,
      h6: 1,
      ol: 1,
      h1: 1,
      h3: 1,
      h2: 1,
      form: 1,
      font: 1,
      '#': 1,
      select: 1,
      menu: 1,
      ins: 1,
      abbr: 1,
      label: 1,
      code: 1,
      table: 1,
      script: 1,
      cite: 1,
      input: 1,
      iframe: 1,
      strong: 1,
      textarea: 1,
      noframes: 1,
      big: 1,
      small: 1,
      span: 1,
      hr: 1,
      sub: 1,
      bdo: 1,
      var: 1,
      div: 1,
      object: 1,
      sup: 1,
      strike: 1,
      dir: 1,
      map: 1,
      dl: 1,
      applet: 1,
      del: 1,
      isindex: 1,
      fieldset: 1,
      ul: 1,
      b: 1,
      acronym: 1,
      a: 1,
      blockquote: 1,
      i: 1,
      u: 1,
      s: 1,
      tt: 1,
      address: 1,
      q: 1,
      pre: 1,
      p: 1,
      em: 1,
      dfn: 1
    })
    var L = X(_({ a: 0 }), J)
    var M = _({ tr: 1 })
    var N = _({ '#': 1 })
    var O = X(_({ param: 1 }), K)
    var P = X(_({ form: 1 }), A, D, E, I)
    var Q = _({ li: 1, ol: 1, ul: 1 })
    var R = _({ style: 1, script: 1 })
    var S = _({ base: 1, link: 1, meta: 1, title: 1 })
    var T = X(S, R)
    var U = _({ head: 1, body: 1 })
    var V = _({ html: 1 })
    var block = _({
      address: 1,
      blockquote: 1,
      center: 1,
      dir: 1,
      div: 1,
      dl: 1,
      fieldset: 1,
      form: 1,
      h1: 1,
      h2: 1,
      h3: 1,
      h4: 1,
      h5: 1,
      h6: 1,
      hr: 1,
      isindex: 1,
      menu: 1,
      noframes: 1,
      ol: 1,
      p: 1,
      pre: 1,
      table: 1,
      ul: 1
    })
    var empty = _({
      area: 1,
      base: 1,
      basefont: 1,
      br: 1,
      col: 1,
      command: 1,
      dialog: 1,
      embed: 1,
      hr: 1,
      img: 1,
      input: 1,
      isindex: 1,
      keygen: 1,
      link: 1,
      meta: 1,
      param: 1,
      source: 1,
      track: 1,
      wbr: 1
    })
    return _({
      $nonBodyContent: X(V, U, S),
      $block: block,
      $inline: L,
      $inlineWithA: X(_({ a: 1 }), L),
      $body: X(_({ script: 1, style: 1 }), block),
      $cdata: _({ script: 1, style: 1 }),
      $empty: empty,
      $nonChild: _({ iframe: 1, textarea: 1 }),
      $listItem: _({ dd: 1, dt: 1, li: 1 }),
      $list: _({ ul: 1, ol: 1, dl: 1 }),
      $isNotEmpty: _({
        table: 1,
        ul: 1,
        ol: 1,
        dl: 1,
        iframe: 1,
        area: 1,
        base: 1,
        col: 1,
        hr: 1,
        img: 1,
        embed: 1,
        input: 1,
        link: 1,
        meta: 1,
        param: 1,
        h1: 1,
        h2: 1,
        h3: 1,
        h4: 1,
        h5: 1,
        h6: 1
      }),
      $removeEmpty: _({
        a: 1,
        abbr: 1,
        acronym: 1,
        address: 1,
        b: 1,
        bdo: 1,
        big: 1,
        cite: 1,
        code: 1,
        del: 1,
        dfn: 1,
        em: 1,
        font: 1,
        i: 1,
        ins: 1,
        label: 1,
        kbd: 1,
        q: 1,
        s: 1,
        samp: 1,
        small: 1,
        span: 1,
        strike: 1,
        strong: 1,
        sub: 1,
        sup: 1,
        tt: 1,
        u: 1,
        var: 1
      }),
      $removeEmptyBlock: _({ p: 1, div: 1 }),
      $tableContent: _({
        caption: 1,
        col: 1,
        colgroup: 1,
        tbody: 1,
        td: 1,
        tfoot: 1,
        th: 1,
        thead: 1,
        tr: 1,
        table: 1
      }),
      $notTransContent: _({ pre: 1, script: 1, style: 1, textarea: 1 }),
      html: U,
      head: T,
      style: N,
      script: N,
      body: P,
      base: {},
      link: {},
      meta: {},
      title: N,
      col: {},
      tr: _({ td: 1, th: 1 }),
      img: {},
      embed: {},
      colgroup: _({ thead: 1, col: 1, tbody: 1, tr: 1, tfoot: 1 }),
      noscript: P,
      td: P,
      br: {},
      th: P,
      center: P,
      kbd: L,
      button: X(I, E),
      basefont: {},
      h5: L,
      h4: L,
      samp: L,
      h6: L,
      ol: Q,
      h1: L,
      h3: L,
      option: N,
      h2: L,
      form: X(A, D, E, I),
      select: _({ optgroup: 1, option: 1 }),
      font: L,
      ins: L,
      menu: Q,
      abbr: L,
      label: L,
      table: _({ thead: 1, col: 1, tbody: 1, tr: 1, colgroup: 1, caption: 1, tfoot: 1 }),
      code: L,
      tfoot: M,
      cite: L,
      li: P,
      input: {},
      iframe: P,
      strong: L,
      textarea: N,
      noframes: P,
      big: L,
      small: L,
      span: _({ '#': 1, br: 1, b: 1, strong: 1, u: 1, i: 1, em: 1, sub: 1, sup: 1, strike: 1, span: 1 }),
      hr: L,
      dt: L,
      sub: L,
      optgroup: _({ option: 1 }),
      param: {},
      bdo: L,
      var: L,
      div: P,
      object: O,
      sup: L,
      dd: P,
      strike: L,
      area: {},
      dir: Q,
      map: X(_({ area: 1, form: 1, p: 1 }), A, F, E),
      applet: O,
      dl: _({ dt: 1, dd: 1 }),
      del: L,
      isindex: {},
      fieldset: X(_({ legend: 1 }), K),
      thead: M,
      ul: Q,
      acronym: L,
      b: L,
      a: X(_({ a: 1 }), J),
      blockquote: X(_({ td: 1, tr: 1, tbody: 1, li: 1 }), P),
      caption: L,
      i: L,
      u: L,
      tbody: M,
      s: L,
      address: X(D, I),
      tt: L,
      legend: L,
      q: L,
      pre: X(G, C),
      p: X(_({ a: 1 }), L),
      em: L,
      dfn: L
    })
  })())

  function getDomNode(node, start, ltr, startFromChild, fn, guard) {
    var tmpNode = startFromChild && node[start]
    var parent
    !tmpNode && (tmpNode = node[ltr])
    while (!tmpNode && (parent = (parent || node).parentNode)) {
      if (parent.tagName == 'BODY' || (guard && !guard(parent))) {
        return null
      }
      tmpNode = parent[ltr]
    }
    if (tmpNode && fn && !fn(tmpNode)) {
      return getDomNode(tmpNode, start, ltr, false, fn)
    }
    return tmpNode
  }

  var attrFix =
    ie && browser.version < 9
      ? {
        tabindex: 'tabIndex',
        readonly: 'readOnly',
        for: 'htmlFor',
        class: 'className',
        maxlength: 'maxLength',
        cellspacing: 'cellSpacing',
        cellpadding: 'cellPadding',
        rowspan: 'rowSpan',
        colspan: 'colSpan',
        usemap: 'useMap',
        frameborder: 'frameBorder'
      }
      : { tabindex: 'tabIndex', readonly: 'readOnly' }
  var styleBlock = utils.listToMap([
    '-webkit-box',
    '-moz-box',
    'block',
    'list-item',
    'table',
    'table-row-group',
    'table-header-group',
    'table-footer-group',
    'table-row',
    'table-column-group',
    'table-column',
    'table-cell',
    'table-caption'
  ])
  var domUtils = (dom.domUtils = {
    NODE_ELEMENT: 1,
    NODE_DOCUMENT: 9,
    NODE_TEXT: 3,
    NODE_COMMENT: 8,
    NODE_DOCUMENT_FRAGMENT: 11,
    POSITION_IDENTICAL: 0,
    POSITION_DISCONNECTED: 1,
    POSITION_FOLLOWING: 2,
    POSITION_PRECEDING: 4,
    POSITION_IS_CONTAINED: 8,
    POSITION_CONTAINS: 16,
    fillChar: ie && browser.version == '6' ? '\ufeff' : '\u200B',
    keys: { 8: 1, 46: 1, 16: 1, 17: 1, 18: 1, 37: 1, 38: 1, 39: 1, 40: 1, 13: 1 },
    getPosition: function(nodeA, nodeB) {
      if (nodeA === nodeB) {
        return 0
      }
      var node
      var parentsA = [nodeA]
      var parentsB = [nodeB]
      node = nodeA
      while ((node = node.parentNode)) {
        if (node === nodeB) {
          return 10
        }
        parentsA.push(node)
      }
      node = nodeB
      while ((node = node.parentNode)) {
        if (node === nodeA) {
          return 20
        }
        parentsB.push(node)
      }
      parentsA.reverse()
      parentsB.reverse()
      if (parentsA[0] !== parentsB[0]) {
        return 1
      }
      var i = -1
      while ((i++, parentsA[i] === parentsB[i])) {
      }
      nodeA = parentsA[i]
      nodeB = parentsB[i]
      while ((nodeA = nodeA.nextSibling)) {
        if (nodeA === nodeB) {
          return 4
        }
      }
      return 2
    },
    getNodeIndex: function(node, ignoreTextNode) {
      var preNode = node
      var i = 0
      while ((preNode = preNode.previousSibling)) {
        if (ignoreTextNode && preNode.nodeType == 3) {
          if (preNode.nodeType != preNode.nextSibling.nodeType) {
            i++
          }
          continue
        }
        i++
      }
      return i
    },
    inDoc: function(node, doc) {
      return domUtils.getPosition(node, doc) == 10
    },
    findParent: function(node, filterFn, includeSelf) {
      if (node && !domUtils.isBody(node)) {
        node = includeSelf ? node : node.parentNode
        while (node) {
          if (!filterFn || filterFn(node) || domUtils.isBody(node)) {
            return filterFn && !filterFn(node) && domUtils.isBody(node) ? null : node
          }
          node = node.parentNode
        }
      }
      return null
    },
    findParentByTagName: function(node, tagNames, includeSelf, excludeFn) {
      tagNames = utils.listToMap(utils.isArray(tagNames) ? tagNames : [tagNames])
      return domUtils.findParent(
        node,
        function(node) {
          return tagNames[node.tagName] && !(excludeFn && excludeFn(node))
        },
        includeSelf
      )
    },
    findParents: function(node, includeSelf, filterFn, closerFirst) {
      var parents = includeSelf && ((filterFn && filterFn(node)) || !filterFn) ? [node] : []
      while ((node = domUtils.findParent(node, filterFn))) {
        parents.push(node)
      }
      return closerFirst ? parents : parents.reverse()
    },
    insertAfter: function(node, newNode) {
      return node.nextSibling
        ? node.parentNode.insertBefore(newNode, node.nextSibling)
        : node.parentNode.appendChild(newNode)
    },
    remove: function(node, keepChildren) {
      var parent = node.parentNode
      var child
      if (parent) {
        if (keepChildren && node.hasChildNodes()) {
          while ((child = node.firstChild)) {
            parent.insertBefore(child, node)
          }
        }
        parent.removeChild(node)
      }
      return node
    },
    getNextDomNode: function(node, startFromChild, filterFn, guard) {
      return getDomNode(node, 'firstChild', 'nextSibling', startFromChild, filterFn, guard)
    },
    getPreDomNode: function(node, startFromChild, filterFn, guard) {
      return getDomNode(node, 'lastChild', 'previousSibling', startFromChild, filterFn, guard)
    },
    isBookmarkNode: function(node) {
      return node.nodeType == 1 && node.id && /^_baidu_bookmark_/i.test(node.id)
    },
    getWindow: function(node) {
      var doc = node.ownerDocument || node
      return doc.defaultView || doc.parentWindow
    },
    getCommonAncestor: function(nodeA, nodeB) {
      if (nodeA === nodeB) {
        return nodeA
      }
      var parentsA = [nodeA]
      var parentsB = [nodeB]
      var parent = nodeA
      var i = -1
      while ((parent = parent.parentNode)) {
        if (parent === nodeB) {
          return parent
        }
        parentsA.push(parent)
      }
      parent = nodeB
      while ((parent = parent.parentNode)) {
        if (parent === nodeA) {
          return parent
        }
        parentsB.push(parent)
      }
      parentsA.reverse()
      parentsB.reverse()
      while ((i++, parentsA[i] === parentsB[i])) {
      }
      return i == 0 ? null : parentsA[i - 1]
    },
    clearEmptySibling: function(node, ignoreNext, ignorePre) {
      function clear(next, dir) {
        var tmpNode
        while (
          next &&
          !domUtils.isBookmarkNode(next) &&
          (domUtils.isEmptyInlineElement(next) ||
            !new RegExp('[^\t\n\r' + domUtils.fillChar + ']').test(next.nodeValue))
          ) {
          tmpNode = next[dir]
          domUtils.remove(next)
          next = tmpNode
        }
      }

      !ignoreNext && clear(node.nextSibling, 'nextSibling')
      !ignorePre && clear(node.previousSibling, 'previousSibling')
    },
    split: function(node, offset) {
      var doc = node.ownerDocument
      if (browser.ie && offset == node.nodeValue.length) {
        var next = doc.createTextNode('')
        return domUtils.insertAfter(node, next)
      }
      var retval = node.splitText(offset)
      if (browser.ie8) {
        var tmpNode = doc.createTextNode('')
        domUtils.insertAfter(retval, tmpNode)
        domUtils.remove(tmpNode)
      }
      return retval
    },
    isWhitespace: function(node) {
      return !new RegExp('[^ \t\n\r' + domUtils.fillChar + ']').test(node.nodeValue)
    },
    getXY: function(element) {
      var x = 0
      var y = 0
      while (element.offsetParent) {
        y += element.offsetTop
        x += element.offsetLeft
        element = element.offsetParent
      }
      return { x: x, y: y }
    },
    on: function(element, type, handler) {
      var types = utils.isArray(type) ? type : utils.trim(type).split(/\s+/)
      var k = types.length
      if (k) {
        while (k--) {
          type = types[k]
          if (element.addEventListener) {
            element.addEventListener(type, handler, false)
          } else {
            if (!handler._d) {
              handler._d = { els: [] }
            }
            var key = type + handler.toString()
            var index = utils.indexOf(handler._d.els, element)
            if (!handler._d[key] || index == -1) {
              if (index == -1) {
                handler._d.els.push(element)
              }
              if (!handler._d[key]) {
                handler._d[key] = function(evt) {
                  return handler.call(evt.srcElement, evt || window.event)
                }
              }
              element.attachEvent('on' + type, handler._d[key])
            }
          }
        }
      }
      element = null
    },
    un: function(element, type, handler) {
      var types = utils.isArray(type) ? type : utils.trim(type).split(/\s+/)
      var k = types.length
      if (k) {
        while (k--) {
          type = types[k]
          if (element.removeEventListener) {
            element.removeEventListener(type, handler, false)
          } else {
            var key = type + handler.toString()
            try {
              element.detachEvent('on' + type, handler._d ? handler._d[key] : handler)
            } catch (e) {
            }
            if (handler._d && handler._d[key]) {
              var index = utils.indexOf(handler._d.els, element)
              if (index != -1) {
                handler._d.els.splice(index, 1)
              }
              handler._d.els.length == 0 && delete handler._d[key]
            }
          }
        }
      }
    },
    isSameElement: function(nodeA, nodeB) {
      if (nodeA.tagName != nodeB.tagName) {
        return false
      }
      var thisAttrs = nodeA.attributes
      var otherAttrs = nodeB.attributes
      if (!ie && thisAttrs.length != otherAttrs.length) {
        return false
      }
      var attrA
      var attrB
      var al = 0
      var bl = 0
      for (var i = 0; (attrA = thisAttrs[i++]);) {
        if (attrA.nodeName == 'style') {
          if (attrA.specified) {
            al++
          }
          if (domUtils.isSameStyle(nodeA, nodeB)) {
            continue
          } else {
            return false
          }
        }
        if (ie) {
          if (attrA.specified) {
            al++
            attrB = otherAttrs.getNamedItem(attrA.nodeName)
          } else {
            continue
          }
        } else {
          attrB = nodeB.attributes[attrA.nodeName]
        }
        if (!attrB.specified || attrA.nodeValue != attrB.nodeValue) {
          return false
        }
      }
      if (ie) {
        for (i = 0; (attrB = otherAttrs[i++]);) {
          if (attrB.specified) {
            bl++
          }
        }
        if (al != bl) {
          return false
        }
      }
      return true
    },
    isSameStyle: function(nodeA, nodeB) {
      var styleA = nodeA.style.cssText.replace(/( ?; ?)/g, ';').replace(/( ?: ?)/g, ':')
      var styleB = nodeB.style.cssText.replace(/( ?; ?)/g, ';').replace(/( ?: ?)/g, ':')
      if (browser.opera) {
        styleA = nodeA.style
        styleB = nodeB.style
        if (styleA.length != styleB.length) {
          return false
        }
        for (var p in styleA) {
          if (/^(\d+|csstext)$/i.test(p)) {
            continue
          }
          if (styleA[p] != styleB[p]) {
            return false
          }
        }
        return true
      }
      if (!styleA || !styleB) {
        return styleA == styleB
      }
      styleA = styleA.split(';')
      styleB = styleB.split(';')
      if (styleA.length != styleB.length) {
        return false
      }
      for (var i = 0, ci; (ci = styleA[i++]);) {
        if (utils.indexOf(styleB, ci) == -1) {
          return false
        }
      }
      return true
    },
    isBlockElm: function(node) {
      return (
        node.nodeType == 1 &&
        (dtd.$block[node.tagName] || styleBlock[domUtils.getComputedStyle(node, 'display')]) &&
        !dtd.$nonChild[node.tagName]
      )
    },
    isBody: function(node) {
      return node && node.nodeType == 1 && node.tagName.toLowerCase() == 'body'
    },
    breakParent: function(node, parent) {
      var tmpNode
      var parentClone = node
      var clone = node
      var leftNodes
      var rightNodes
      do {
        parentClone = parentClone.parentNode
        if (leftNodes) {
          tmpNode = parentClone.cloneNode(false)
          tmpNode.appendChild(leftNodes)
          leftNodes = tmpNode
          tmpNode = parentClone.cloneNode(false)
          tmpNode.appendChild(rightNodes)
          rightNodes = tmpNode
        } else {
          leftNodes = parentClone.cloneNode(false)
          rightNodes = leftNodes.cloneNode(false)
        }
        while ((tmpNode = clone.previousSibling)) {
          leftNodes.insertBefore(tmpNode, leftNodes.firstChild)
        }
        while ((tmpNode = clone.nextSibling)) {
          rightNodes.appendChild(tmpNode)
        }
        clone = parentClone
      } while (parent !== parentClone)
      tmpNode = parent.parentNode
      tmpNode.insertBefore(leftNodes, parent)
      tmpNode.insertBefore(rightNodes, parent)
      tmpNode.insertBefore(node, rightNodes)
      domUtils.remove(parent)
      return node
    },
    isEmptyInlineElement: function(node) {
      if (node.nodeType != 1 || !dtd.$removeEmpty[node.tagName]) {
        return 0
      }
      node = node.firstChild
      while (node) {
        if (domUtils.isBookmarkNode(node)) {
          return 0
        }
        if (
          (node.nodeType == 1 && !domUtils.isEmptyInlineElement(node)) ||
          (node.nodeType == 3 && !domUtils.isWhitespace(node))
        ) {
          return 0
        }
        node = node.nextSibling
      }
      return 1
    },
    trimWhiteTextNode: function(node) {
      function remove(dir) {
        var child
        while ((child = node[dir]) && child.nodeType == 3 && domUtils.isWhitespace(child)) {
          node.removeChild(child)
        }
      }

      remove('firstChild')
      remove('lastChild')
    },
    mergeChild: function(node, tagName, attrs) {
      var list = domUtils.getElementsByTagName(node, node.tagName.toLowerCase())
      for (var i = 0, ci; (ci = list[i++]);) {
        if (!ci.parentNode || domUtils.isBookmarkNode(ci)) {
          continue
        }
        if (ci.tagName.toLowerCase() == 'span') {
          if (node === ci.parentNode) {
            domUtils.trimWhiteTextNode(node)
            if (node.childNodes.length == 1) {
              node.style.cssText = ci.style.cssText + ';' + node.style.cssText
              domUtils.remove(ci, true)
              continue
            }
          }
          ci.style.cssText = node.style.cssText + ';' + ci.style.cssText
          if (attrs) {
            var style = attrs.style
            if (style) {
              style = style.split(';')
              for (var j = 0, s; (s = style[j++]);) {
                ci.style[utils.cssStyleToDomStyle(s.split(':')[0])] = s.split(':')[1]
              }
            }
          }
          if (domUtils.isSameStyle(ci, node)) {
            domUtils.remove(ci, true)
          }
          continue
        }
        if (domUtils.isSameElement(node, ci)) {
          domUtils.remove(ci, true)
        }
      }
    },
    getElementsByTagName: function(node, name, filter) {
      if (filter && utils.isString(filter)) {
        var className = filter
        filter = function(node) {
          return domUtils.hasClass(node, className)
        }
      }
      name = utils
        .trim(name)
        .replace(/[ ]{2,}/g, ' ')
        .split(' ')
      var arr = []
      for (var n = 0, ni; (ni = name[n++]);) {
        var list = node.getElementsByTagName(ni)
        for (var i = 0, ci; (ci = list[i++]);) {
          if (!filter || filter(ci)) {
            arr.push(ci)
          }
        }
      }
      return arr
    },
    mergeToParent: function(node) {
      var parent = node.parentNode
      while (parent && dtd.$removeEmpty[parent.tagName]) {
        if (parent.tagName == node.tagName || parent.tagName == 'A') {
          domUtils.trimWhiteTextNode(parent)
          if (
            (parent.tagName == 'SPAN' && !domUtils.isSameStyle(parent, node)) ||
            (parent.tagName == 'A' && node.tagName == 'SPAN')
          ) {
            if (parent.childNodes.length > 1 || parent !== node.parentNode) {
              node.style.cssText = parent.style.cssText + ';' + node.style.cssText
              parent = parent.parentNode
              continue
            } else {
              parent.style.cssText += ';' + node.style.cssText
              if (parent.tagName == 'A') {
                parent.style.textDecoration = 'underline'
              }
            }
          }
          if (parent.tagName != 'A') {
            parent === node.parentNode && domUtils.remove(node, true)
            break
          }
        }
        parent = parent.parentNode
      }
    },
    mergeSibling: function(node, ignorePre, ignoreNext) {
      function merge(rtl, start, node) {
        var next
        if (
          (next = node[rtl]) &&
          !domUtils.isBookmarkNode(next) &&
          next.nodeType == 1 &&
          domUtils.isSameElement(node, next)
        ) {
          while (next.firstChild) {
            if (start == 'firstChild') {
              node.insertBefore(next.lastChild, node.firstChild)
            } else {
              node.appendChild(next.firstChild)
            }
          }
          domUtils.remove(next)
        }
      }

      !ignorePre && merge('previousSibling', 'firstChild', node)
      !ignoreNext && merge('nextSibling', 'lastChild', node)
    },
    unSelectable:
      (ie && browser.ie9below) || browser.opera
        ? function(node) {
          node.onselectstart = function() {
            return false
          }
          node.onclick = node.onkeyup = node.onkeydown = function() {
            return false
          }
          node.unselectable = 'on'
          node.setAttribute('unselectable', 'on')
          for (var i = 0, ci; (ci = node.all[i++]);) {
            switch (ci.tagName.toLowerCase()) {
              case 'iframe':
              case 'textarea':
              case 'input':
              case 'select':
                break
              default:
                ci.unselectable = 'on'
                node.setAttribute('unselectable', 'on')
            }
          }
        }
        : function(node) {
          node.style.MozUserSelect = node.style.webkitUserSelect = node.style.msUserSelect = node.style.KhtmlUserSelect =
            'none'
        },
    removeAttributes: function(node, attrNames) {
      attrNames = utils.isArray(attrNames)
        ? attrNames
        : utils
          .trim(attrNames)
          .replace(/[ ]{2,}/g, ' ')
          .split(' ')
      for (var i = 0, ci; (ci = attrNames[i++]);) {
        ci = attrFix[ci] || ci
        switch (ci) {
          case 'className':
            node[ci] = ''
            break
          case 'style':
            node.style.cssText = ''
            var val = node.getAttributeNode('style')
            !browser.ie && val && node.removeAttributeNode(val)
        }
        node.removeAttribute(ci)
      }
    },
    standardAttributes: function(node, attrNames) {
      attrNames = utils.isArray(attrNames)
        ? attrNames
        : utils
          .trim(attrNames)
          .replace(/[ ]{2,}/g, ' ')
          .split(' ')
      for (var i = 0, ci; (ci = attrNames[i++]);) {
        ci = attrFix[ci] || ci
        console.log('standardAttributes-class', node)
        switch (ci) {
          case 'style':
            node.style.setProperty('font-size', '18px')
            node.style.setProperty('line-height', '1.75em')
            node.style.setProperty('font-family', '宋体')
            node.style.setProperty('color', 'rgba(0, 0, 0, 0.65)')
          // !browser.ie && val && node.removeAttributeNode(val)
        }
        // node.removeAttribute(ci)
      }
    },
    createElement: function(doc, tag, attrs) {
      return domUtils.setAttributes(doc.createElement(tag), attrs)
    },
    setAttributes: function(node, attrs) {
      for (var attr in attrs) {
        if (attrs.hasOwnProperty(attr)) {
          var value = attrs[attr]
          switch (attr) {
            case 'class':
              node.className = value
              break
            case 'style':
              node.style.cssText = node.style.cssText + ';' + value
              break
            case 'innerHTML':
              node[attr] = value
              break
            case 'value':
              node.value = value
              break
            default:
              node.setAttribute(attrFix[attr] || attr, value)
          }
        }
      }
      return node
    },
    getComputedStyle: function(element, styleName) {
      var pros = 'width height top left'
      if (pros.indexOf(styleName) > -1) {
        return (
          element[
          'offset' +
          styleName.replace(/^\w/, function(s) {
            return s.toUpperCase()
          })
            ] + 'px'
        )
      }
      if (element.nodeType == 3) {
        element = element.parentNode
      }
      if (
        browser.ie &&
        browser.version < 9 &&
        styleName == 'font-size' &&
        !element.style.fontSize &&
        !dtd.$empty[element.tagName] &&
        !dtd.$nonChild[element.tagName]
      ) {
        var span = element.ownerDocument.createElement('span')
        span.style.cssText = 'padding:0;border:0;font-family:simsun;'
        span.innerHTML = '.'
        element.appendChild(span)
        var result = span.offsetHeight
        element.removeChild(span)
        span = null
        return result + 'px'
      }
      try {
        var value =
          domUtils.getStyle(element, styleName) ||
          (window.getComputedStyle
            ? domUtils
              .getWindow(element)
              .getComputedStyle(element, '')
              .getPropertyValue(styleName)
            : (element.currentStyle || element.style)[utils.cssStyleToDomStyle(styleName)])
      } catch (e) {
        return ''
      }
      return utils.transUnitToPx(utils.fixColor(styleName, value))
    },
    removeClasses: function(elm, classNames) {
      classNames = utils.isArray(classNames)
        ? classNames
        : utils
          .trim(classNames)
          .replace(/[ ]{2,}/g, ' ')
          .split(' ')
      for (var i = 0, ci, cls = elm.className; (ci = classNames[i++]);) {
        cls = cls.replace(new RegExp('\\b' + ci + '\\b'), '')
      }
      cls = utils.trim(cls).replace(/[ ]{2,}/g, ' ')
      if (cls) {
        elm.className = cls
      } else {
        domUtils.removeAttributes(elm, ['class'])
      }
    },
    addClass: function(elm, classNames) {
      if (!elm) {
        return
      }
      classNames = utils
        .trim(classNames)
        .replace(/[ ]{2,}/g, ' ')
        .split(' ')
      for (var i = 0, ci, cls = elm.className; (ci = classNames[i++]);) {
        if (!new RegExp('\\b' + ci + '\\b').test(cls)) {
          cls += ' ' + ci
        }
      }
      elm.className = utils.trim(cls)
    },
    hasClass: function(element, className) {
      if (utils.isRegExp(className)) {
        return className.test(element.className)
      }
      className = utils
        .trim(className)
        .replace(/[ ]{2,}/g, ' ')
        .split(' ')
      for (var i = 0, ci, cls = element.className; (ci = className[i++]);) {
        if (!new RegExp('\\b' + ci + '\\b', 'i').test(cls)) {
          return false
        }
      }
      return i - 1 == className.length
    },
    preventDefault: function(evt) {
      evt.preventDefault ? evt.preventDefault() : (evt.returnValue = false)
    },
    removeStyle: function(element, name) {
      if (browser.ie) {
        if (name == 'color') {
          name = '(^|;)' + name
        }
        element.style.cssText = element.style.cssText.replace(new RegExp(name + '[^:]*:[^;]+;?', 'ig'), '')
      } else {
        if (element.style.removeProperty) {
          element.style.removeProperty(name)
        } else {
          element.style.removeAttribute(utils.cssStyleToDomStyle(name))
        }
      }
      if (!element.style.cssText) {
        domUtils.removeAttributes(element, ['style'])
      }
    },
    getStyle: function(element, name) {
      var value = element.style[utils.cssStyleToDomStyle(name)]
      return utils.fixColor(name, value)
    },
    setStyle: function(element, name, value) {
      element.style[utils.cssStyleToDomStyle(name)] = value
      if (!utils.trim(element.style.cssText)) {
        this.removeAttributes(element, 'style')
      }
    },
    setStyles: function(element, styles) {
      for (var name in styles) {
        if (styles.hasOwnProperty(name)) {
          domUtils.setStyle(element, name, styles[name])
        }
      }
    },
    removeDirtyAttr: function(node) {
      for (var i = 0, ci, nodes = node.getElementsByTagName('*'); (ci = nodes[i++]);) {
        ci.removeAttribute('_moz_dirty')
      }
      node.removeAttribute('_moz_dirty')
    },
    getChildCount: function(node, fn) {
      var count = 0
      var first = node.firstChild
      fn =
        fn ||
        function() {
          return 1
        }
      while (first) {
        if (fn(first)) {
          count++
        }
        first = first.nextSibling
      }
      return count
    },
    isEmptyNode: function(node) {
      return (
        !node.firstChild ||
        domUtils.getChildCount(node, function(node) {
          return !domUtils.isBr(node) && !domUtils.isBookmarkNode(node) && !domUtils.isWhitespace(node)
        }) == 0
      )
    },
    clearSelectedArr: function(nodes) {
      var node
      while ((node = nodes.pop())) {
        domUtils.removeAttributes(node, ['class'])
      }
    },
    scrollToView: function(node, win, offsetTop) {
      var getViewPaneSize = function() {
        var doc = win.document
        var mode = doc.compatMode == 'CSS1Compat'
        return {
          width: (mode ? doc.documentElement.clientWidth : doc.body.clientWidth) || 0,
          height: (mode ? doc.documentElement.clientHeight : doc.body.clientHeight) || 0
        }
      }
      var getScrollPosition = function(win) {
        if ('pageXOffset' in win) {
          return { x: win.pageXOffset || 0, y: win.pageYOffset || 0 }
        } else {
          var doc = win.document
          return {
            x: doc.documentElement.scrollLeft || doc.body.scrollLeft || 0,
            y: doc.documentElement.scrollTop || doc.body.scrollTop || 0
          }
        }
      }
      var winHeight = getViewPaneSize().height
      var offset = winHeight * -1 + offsetTop
      offset += node.offsetHeight || 0
      var elementPosition = domUtils.getXY(node)
      offset += elementPosition.y
      var currentScroll = getScrollPosition(win).y
      if (offset > currentScroll || offset < currentScroll - winHeight) {
        win.scrollTo(0, offset + (offset < 0 ? -20 : 20))
      }
    },
    isBr: function(node) {
      return node.nodeType == 1 && node.tagName == 'BR'
    },
    isFillChar: function(node, isInStart) {
      if (node.nodeType != 3) {
        return false
      }
      var text = node.nodeValue
      if (isInStart) {
        return new RegExp('^' + domUtils.fillChar).test(text)
      }
      return !text.replace(new RegExp(domUtils.fillChar, 'g'), '').length
    },
    isStartInblock: function(range) {
      var tmpRange = range.cloneRange()
      var flag = 0
      var start = tmpRange.startContainer
      var tmp
      if (start.nodeType == 1 && start.childNodes[tmpRange.startOffset]) {
        start = start.childNodes[tmpRange.startOffset]
        var pre = start.previousSibling
        while (pre && domUtils.isFillChar(pre)) {
          start = pre
          pre = pre.previousSibling
        }
      }
      if (this.isFillChar(start, true) && tmpRange.startOffset == 1) {
        tmpRange.setStartBefore(start)
        start = tmpRange.startContainer
      }
      while (start && domUtils.isFillChar(start)) {
        tmp = start
        start = start.previousSibling
      }
      if (tmp) {
        tmpRange.setStartBefore(tmp)
        start = tmpRange.startContainer
      }
      if (start.nodeType == 1 && domUtils.isEmptyNode(start) && tmpRange.startOffset == 1) {
        tmpRange.setStart(start, 0).collapse(true)
      }
      while (!tmpRange.startOffset) {
        start = tmpRange.startContainer
        if (domUtils.isBlockElm(start) || domUtils.isBody(start)) {
          flag = 1
          break
        }
        var pre = tmpRange.startContainer.previousSibling
        var tmpNode
        if (!pre) {
          tmpRange.setStartBefore(tmpRange.startContainer)
        } else {
          while (pre && domUtils.isFillChar(pre)) {
            tmpNode = pre
            pre = pre.previousSibling
          }
          if (tmpNode) {
            tmpRange.setStartBefore(tmpNode)
          } else {
            tmpRange.setStartBefore(tmpRange.startContainer)
          }
        }
      }
      return flag && !domUtils.isBody(tmpRange.startContainer) ? 1 : 0
    },
    isEmptyBlock: function(node, reg) {
      if (node.nodeType != 1) {
        return 0
      }
      reg = reg || new RegExp('[ \xa0\t\r\n' + domUtils.fillChar + ']', 'g')
      if (node[browser.ie ? 'innerText' : 'textContent'].replace(reg, '').length > 0) {
        return 0
      }
      for (var n in dtd.$isNotEmpty) {
        if (node.getElementsByTagName(n).length) {
          return 0
        }
      }
      return 1
    },
    setViewportOffset: function(element, offset) {
      var left = parseInt(element.style.left) | 0
      var top = parseInt(element.style.top) | 0
      var rect = element.getBoundingClientRect()
      var offsetLeft = offset.left - rect.left
      var offsetTop = offset.top - rect.top
      if (offsetLeft) {
        element.style.left = left + offsetLeft + 'px'
      }
      if (offsetTop) {
        element.style.top = top + offsetTop + 'px'
      }
    },
    fillNode: function(doc, node) {
      var tmpNode = browser.ie ? doc.createTextNode(domUtils.fillChar) : doc.createElement('br')
      node.innerHTML = ''
      node.appendChild(tmpNode)
    },
    moveChild: function(src, tag, dir) {
      while (src.firstChild) {
        if (dir && tag.firstChild) {
          tag.insertBefore(src.lastChild, tag.firstChild)
        } else {
          tag.appendChild(src.firstChild)
        }
      }
    },
    hasNoAttributes: function(node) {
      return browser.ie ? /^<\w+\s*?>/.test(node.outerHTML) : node.attributes.length == 0
    },
    isCustomeNode: function(node) {
      return node.nodeType == 1 && node.getAttribute('_ue_custom_node_')
    },
    isTagNode: function(node, tagNames) {
      return node.nodeType == 1 && new RegExp('\\b' + node.tagName + '\\b', 'i').test(tagNames)
    },
    filterNodeList: function(nodelist, filter, forAll) {
      var results = []
      if (!utils.isFunction(filter)) {
        var str = filter
        filter = function(n) {
          return utils.indexOf(utils.isArray(str) ? str : str.split(' '), n.tagName.toLowerCase()) != -1
        }
      }
      utils.each(nodelist, function(n) {
        filter(n) && results.push(n)
      })
      return results.length == 0 ? null : results.length == 1 || !forAll ? results[0] : results
    },
    isInNodeEndBoundary: function(rng, node) {
      var start = rng.startContainer
      if (start.nodeType == 3 && rng.startOffset != start.nodeValue.length) {
        return 0
      }
      if (start.nodeType == 1 && rng.startOffset != start.childNodes.length) {
        return 0
      }
      while (start !== node) {
        if (start.nextSibling) {
          return 0
        }
        start = start.parentNode
      }
      return 1
    },
    isBoundaryNode: function(node, dir) {
      var tmp
      while (!domUtils.isBody(node)) {
        tmp = node
        node = node.parentNode
        if (tmp !== node[dir]) {
          return false
        }
      }
      return true
    },
    fillHtml: browser.ie11below ? '&nbsp;' : '<br/>',
    filterImgHtml: function(domReplace) {
      let domReg = /<img.*?(?:>|\/>)/gi
      return domReplace.replace(domReg, (str) => {
        console.log('替换图片', str)
        if (str.indexOf('https://cxzdjs.oss-cn-hangzhou.aliyuncs.com') > -1) {
          return str
        }
        return ''
      })
    }
  })
  var fillCharReg = new RegExp(domUtils.fillChar, 'g')
  ;(function() {
    var guid = 0
    var fillChar = domUtils.fillChar
    var fillData

    function updateCollapse(range) {
      range.collapsed =
        range.startContainer &&
        range.endContainer &&
        range.startContainer === range.endContainer &&
        range.startOffset == range.endOffset
    }

    function selectOneNode(rng) {
      return (
        !rng.collapsed &&
        rng.startContainer.nodeType == 1 &&
        rng.startContainer === rng.endContainer &&
        rng.endOffset - rng.startOffset == 1
      )
    }

    function setEndPoint(toStart, node, offset, range) {
      if (node.nodeType == 1 && (dtd.$empty[node.tagName] || dtd.$nonChild[node.tagName])) {
        offset = domUtils.getNodeIndex(node) + (toStart ? 0 : 1)
        node = node.parentNode
      }
      if (toStart) {
        range.startContainer = node
        range.startOffset = offset
        if (!range.endContainer) {
          range.collapse(true)
        }
      } else {
        range.endContainer = node
        range.endOffset = offset
        if (!range.startContainer) {
          range.collapse(false)
        }
      }
      updateCollapse(range)
      return range
    }

    function execContentsAction(range, action) {
      var start = range.startContainer
      var end = range.endContainer
      var startOffset = range.startOffset
      var endOffset = range.endOffset
      var doc = range.document
      var frag = doc.createDocumentFragment()
      var tmpStart
      var tmpEnd
      if (start.nodeType == 1) {
        start = start.childNodes[startOffset] || (tmpStart = start.appendChild(doc.createTextNode('')))
      }
      if (end.nodeType == 1) {
        end = end.childNodes[endOffset] || (tmpEnd = end.appendChild(doc.createTextNode('')))
      }
      if (start === end && start.nodeType == 3) {
        frag.appendChild(doc.createTextNode(start.substringData(startOffset, endOffset - startOffset)))
        if (action) {
          start.deleteData(startOffset, endOffset - startOffset)
          range.collapse(true)
        }
        return frag
      }
      var current
      var currentLevel
      var clone = frag
      var startParents = domUtils.findParents(start, true)
      var endParents = domUtils.findParents(end, true)
      for (var i = 0; startParents[i] == endParents[i];) {
        i++
      }
      for (var j = i, si; (si = startParents[j]); j++) {
        current = si.nextSibling
        if (si == start) {
          if (!tmpStart) {
            if (range.startContainer.nodeType == 3) {
              clone.appendChild(doc.createTextNode(start.nodeValue.slice(startOffset)))
              if (action) {
                start.deleteData(startOffset, start.nodeValue.length - startOffset)
              }
            } else {
              clone.appendChild(!action ? start.cloneNode(true) : start)
            }
          }
        } else {
          currentLevel = si.cloneNode(false)
          clone.appendChild(currentLevel)
        }
        while (current) {
          if (current === end || current === endParents[j]) {
            break
          }
          si = current.nextSibling
          clone.appendChild(!action ? current.cloneNode(true) : current)
          current = si
        }
        clone = currentLevel
      }
      clone = frag
      if (!startParents[i]) {
        clone.appendChild(startParents[i - 1].cloneNode(false))
        clone = clone.firstChild
      }
      for (var j = i, ei; (ei = endParents[j]); j++) {
        current = ei.previousSibling
        if (ei == end) {
          if (!tmpEnd && range.endContainer.nodeType == 3) {
            clone.appendChild(doc.createTextNode(end.substringData(0, endOffset)))
            if (action) {
              end.deleteData(0, endOffset)
            }
          }
        } else {
          currentLevel = ei.cloneNode(false)
          clone.appendChild(currentLevel)
        }
        if (j != i || !startParents[i]) {
          while (current) {
            if (current === start) {
              break
            }
            ei = current.previousSibling
            clone.insertBefore(!action ? current.cloneNode(true) : current, clone.firstChild)
            current = ei
          }
        }
        clone = currentLevel
      }
      if (action) {
        range
          .setStartBefore(!endParents[i] ? endParents[i - 1] : !startParents[i] ? startParents[i - 1] : endParents[i])
          .collapse(true)
      }
      tmpStart && domUtils.remove(tmpStart)
      tmpEnd && domUtils.remove(tmpEnd)
      return frag
    }

    var Range = (dom.Range = function(document) {
      var me = this
      me.startContainer = me.startOffset = me.endContainer = me.endOffset = null
      me.document = document
      me.collapsed = true
    })

    function removeFillData(doc, excludeNode) {
      try {
        if (fillData && domUtils.inDoc(fillData, doc)) {
          if (!fillData.nodeValue.replace(fillCharReg, '').length) {
            var tmpNode = fillData.parentNode
            domUtils.remove(fillData)
            while (
              tmpNode &&
              domUtils.isEmptyInlineElement(tmpNode) &&
              (browser.safari
                ? !(domUtils.getPosition(tmpNode, excludeNode) & domUtils.POSITION_CONTAINS)
                : !tmpNode.contains(excludeNode))
              ) {
              fillData = tmpNode.parentNode
              domUtils.remove(tmpNode)
              tmpNode = fillData
            }
          } else {
            fillData.nodeValue = fillData.nodeValue.replace(fillCharReg, '')
          }
        }
      } catch (e) {
      }
    }

    function mergeSibling(node, dir) {
      var tmpNode
      node = node[dir]
      while (node && domUtils.isFillChar(node)) {
        tmpNode = node[dir]
        domUtils.remove(node)
        node = tmpNode
      }
    }

    Range.prototype = {
      cloneContents: function() {
        return this.collapsed ? null : execContentsAction(this, 0)
      },
      deleteContents: function() {
        var txt
        if (!this.collapsed) {
          execContentsAction(this, 1)
        }
        if (browser.webkit) {
          txt = this.startContainer
          if (txt.nodeType == 3 && !txt.nodeValue.length) {
            this.setStartBefore(txt).collapse(true)
            domUtils.remove(txt)
          }
        }
        return this
      },
      extractContents: function() {
        return this.collapsed ? null : execContentsAction(this, 2)
      },
      setStart: function(node, offset) {
        return setEndPoint(true, node, offset, this)
      },
      setEnd: function(node, offset) {
        return setEndPoint(false, node, offset, this)
      },
      setStartAfter: function(node) {
        return this.setStart(node.parentNode, domUtils.getNodeIndex(node) + 1)
      },
      setStartBefore: function(node) {
        return this.setStart(node.parentNode, domUtils.getNodeIndex(node))
      },
      setEndAfter: function(node) {
        return this.setEnd(node.parentNode, domUtils.getNodeIndex(node) + 1)
      },
      setEndBefore: function(node) {
        return this.setEnd(node.parentNode, domUtils.getNodeIndex(node))
      },
      setStartAtFirst: function(node) {
        return this.setStart(node, 0)
      },
      setStartAtLast: function(node) {
        return this.setStart(node, node.nodeType == 3 ? node.nodeValue.length : node.childNodes.length)
      },
      setEndAtFirst: function(node) {
        return this.setEnd(node, 0)
      },
      setEndAtLast: function(node) {
        return this.setEnd(node, node.nodeType == 3 ? node.nodeValue.length : node.childNodes.length)
      },
      selectNode: function(node) {
        return this.setStartBefore(node).setEndAfter(node)
      },
      selectNodeContents: function(node) {
        return this.setStart(node, 0).setEndAtLast(node)
      },
      cloneRange: function() {
        var me = this
        return new Range(me.document).setStart(me.startContainer, me.startOffset).setEnd(me.endContainer, me.endOffset)
      },
      collapse: function(toStart) {
        var me = this
        if (toStart) {
          me.endContainer = me.startContainer
          me.endOffset = me.startOffset
        } else {
          me.startContainer = me.endContainer
          me.startOffset = me.endOffset
        }
        me.collapsed = true
        return me
      },
      shrinkBoundary: function(ignoreEnd) {
        var me = this
        var child
        var collapsed = me.collapsed

        function check(node) {
          return (
            node.nodeType == 1 &&
            !domUtils.isBookmarkNode(node) &&
            !dtd.$empty[node.tagName] &&
            !dtd.$nonChild[node.tagName]
          )
        }

        while (
          me.startContainer.nodeType == 1 &&
          (child = me.startContainer.childNodes[me.startOffset]) &&
          check(child)
          ) {
          me.setStart(child, 0)
        }
        if (collapsed) {
          return me.collapse(true)
        }
        if (!ignoreEnd) {
          while (
            me.endContainer.nodeType == 1 &&
            me.endOffset > 0 &&
            (child = me.endContainer.childNodes[me.endOffset - 1]) &&
            check(child)
            ) {
            me.setEnd(child, child.childNodes.length)
          }
        }
        return me
      },
      getCommonAncestor: function(includeSelf, ignoreTextNode) {
        var me = this
        var start = me.startContainer
        var end = me.endContainer
        if (start === end) {
          if (includeSelf && selectOneNode(this)) {
            start = start.childNodes[me.startOffset]
            if (start.nodeType == 1) {
              return start
            }
          }
          return ignoreTextNode && start.nodeType == 3 ? start.parentNode : start
        }
        return domUtils.getCommonAncestor(start, end)
      },
      trimBoundary: function(ignoreEnd) {
        this.txtToElmBoundary()
        var start = this.startContainer
        var offset = this.startOffset
        var collapsed = this.collapsed
        var end = this.endContainer
        if (start.nodeType == 3) {
          if (offset == 0) {
            this.setStartBefore(start)
          } else {
            if (offset >= start.nodeValue.length) {
              this.setStartAfter(start)
            } else {
              var textNode = domUtils.split(start, offset)
              if (start === end) {
                this.setEnd(textNode, this.endOffset - offset)
              } else {
                if (start.parentNode === end) {
                  this.endOffset += 1
                }
              }
              this.setStartBefore(textNode)
            }
          }
          if (collapsed) {
            return this.collapse(true)
          }
        }
        if (!ignoreEnd) {
          offset = this.endOffset
          end = this.endContainer
          if (end.nodeType == 3) {
            if (offset == 0) {
              this.setEndBefore(end)
            } else {
              offset < end.nodeValue.length && domUtils.split(end, offset)
              this.setEndAfter(end)
            }
          }
        }
        return this
      },
      txtToElmBoundary: function(ignoreCollapsed) {
        function adjust(r, c) {
          var container = r[c + 'Container']
          var offset = r[c + 'Offset']
          if (container.nodeType == 3) {
            if (!offset) {
              r[
              'set' +
              c.replace(/(\w)/, function(a) {
                return a.toUpperCase()
              }) +
              'Before'
                ](container)
            } else {
              if (offset >= container.nodeValue.length) {
                r[
                'set' +
                c.replace(/(\w)/, function(a) {
                  return a.toUpperCase()
                }) +
                'After'
                  ](container)
              }
            }
          }
        }

        if (ignoreCollapsed || !this.collapsed) {
          adjust(this, 'start')
          adjust(this, 'end')
        }
        return this
      },
      insertNode: function(node) {
        var first = node
        var length = 1
        if (node.nodeType == 11) {
          first = node.firstChild
          length = node.childNodes.length
        }
        this.trimBoundary(true)
        var start = this.startContainer
        var offset = this.startOffset
        var nextNode = start.childNodes[offset]
        if (nextNode) {
          start.insertBefore(node, nextNode)
        } else {
          start.appendChild(node)
        }
        if (first.parentNode === this.endContainer) {
          this.endOffset = this.endOffset + length
        }
        return this.setStartBefore(first)
      },
      setCursor: function(toEnd, noFillData) {
        return this.collapse(!toEnd).select(noFillData)
      },
      createBookmark: function(serialize, same) {
        var endNode
        var startNode = this.document.createElement('span')
        startNode.style.cssText = 'display:none;line-height:0px;'
        startNode.appendChild(this.document.createTextNode('\u200D'))
        startNode.id = '_baidu_bookmark_start_' + (same ? '' : guid++)
        if (!this.collapsed) {
          endNode = startNode.cloneNode(true)
          endNode.id = '_baidu_bookmark_end_' + (same ? '' : guid++)
        }
        this.insertNode(startNode)
        if (endNode) {
          this.collapse()
            .insertNode(endNode)
            .setEndBefore(endNode)
        }
        this.setStartAfter(startNode)
        return {
          start: serialize ? startNode.id : startNode,
          end: endNode ? (serialize ? endNode.id : endNode) : null,
          id: serialize
        }
      },
      moveToBookmark: function(bookmark) {
        var start = bookmark.id ? this.document.getElementById(bookmark.start) : bookmark.start
        var end = bookmark.end && bookmark.id ? this.document.getElementById(bookmark.end) : bookmark.end
        this.setStartBefore(start)
        domUtils.remove(start)
        if (end) {
          this.setEndBefore(end)
          domUtils.remove(end)
        } else {
          this.collapse(true)
        }
        return this
      },
      enlarge: function(toBlock, stopFn) {
        var isBody = domUtils.isBody
        var pre
        var node
        var tmp = this.document.createTextNode('')
        if (toBlock) {
          node = this.startContainer
          if (node.nodeType == 1) {
            if (node.childNodes[this.startOffset]) {
              pre = node = node.childNodes[this.startOffset]
            } else {
              node.appendChild(tmp)
              pre = node = tmp
            }
          } else {
            pre = node
          }
          while (1) {
            if (domUtils.isBlockElm(node)) {
              node = pre
              while ((pre = node.previousSibling) && !domUtils.isBlockElm(pre)) {
                node = pre
              }
              this.setStartBefore(node)
              break
            }
            pre = node
            node = node.parentNode
          }
          node = this.endContainer
          if (node.nodeType == 1) {
            if ((pre = node.childNodes[this.endOffset])) {
              node.insertBefore(tmp, pre)
            } else {
              node.appendChild(tmp)
            }
            pre = node = tmp
          } else {
            pre = node
          }
          while (1) {
            if (domUtils.isBlockElm(node)) {
              node = pre
              while ((pre = node.nextSibling) && !domUtils.isBlockElm(pre)) {
                node = pre
              }
              this.setEndAfter(node)
              break
            }
            pre = node
            node = node.parentNode
          }
          if (tmp.parentNode === this.endContainer) {
            this.endOffset--
          }
          domUtils.remove(tmp)
        }
        if (!this.collapsed) {
          while (this.startOffset == 0) {
            if (stopFn && stopFn(this.startContainer)) {
              break
            }
            if (isBody(this.startContainer)) {
              break
            }
            this.setStartBefore(this.startContainer)
          }
          while (
            this.endOffset ==
            (this.endContainer.nodeType == 1 ? this.endContainer.childNodes.length : this.endContainer.nodeValue.length)
            ) {
            if (stopFn && stopFn(this.endContainer)) {
              break
            }
            if (isBody(this.endContainer)) {
              break
            }
            this.setEndAfter(this.endContainer)
          }
        }
        return this
      },
      enlargeToBlockElm: function(ignoreEnd) {
        while (!domUtils.isBlockElm(this.startContainer)) {
          this.setStartBefore(this.startContainer)
        }
        if (!ignoreEnd) {
          while (!domUtils.isBlockElm(this.endContainer)) {
            this.setEndAfter(this.endContainer)
          }
        }
        return this
      },
      adjustmentBoundary: function() {
        if (!this.collapsed) {
          while (
            !domUtils.isBody(this.startContainer) &&
            this.startOffset ==
            this.startContainer[this.startContainer.nodeType == 3 ? 'nodeValue' : 'childNodes'].length &&
            this.startContainer[this.startContainer.nodeType == 3 ? 'nodeValue' : 'childNodes'].length
            ) {
            this.setStartAfter(this.startContainer)
          }
          while (
            !domUtils.isBody(this.endContainer) &&
            !this.endOffset &&
            this.endContainer[this.endContainer.nodeType == 3 ? 'nodeValue' : 'childNodes'].length
            ) {
            this.setEndBefore(this.endContainer)
          }
        }
        return this
      },
      applyInlineStyle: function(tagName, attrs, list) {
        if (this.collapsed) {
          return this
        }
        this.trimBoundary()
          .enlarge(false, function(node) {
            return node.nodeType == 1 && domUtils.isBlockElm(node)
          })
          .adjustmentBoundary()
        var bookmark = this.createBookmark()
        var end = bookmark.end
        var filterFn = function(node) {
          return node.nodeType == 1 ? node.tagName.toLowerCase() != 'br' : !domUtils.isWhitespace(node)
        }
        var current = domUtils.getNextDomNode(bookmark.start, false, filterFn)
        var node
        var pre
        var range = this.cloneRange()
        while (current && domUtils.getPosition(current, end) & domUtils.POSITION_PRECEDING) {
          if (current.nodeType == 3 || dtd[tagName][current.tagName]) {
            range.setStartBefore(current)
            node = current
            while (node && (node.nodeType == 3 || dtd[tagName][node.tagName]) && node !== end) {
              pre = node
              node = domUtils.getNextDomNode(node, node.nodeType == 1, null, function(parent) {
                return dtd[tagName][parent.tagName]
              })
            }
            var frag = range.setEndAfter(pre).extractContents()
            var elm
            if (list && list.length > 0) {
              var level, top
              top = level = list[0].cloneNode(false)
              for (var i = 1, ci; (ci = list[i++]);) {
                level.appendChild(ci.cloneNode(false))
                level = level.firstChild
              }
              elm = level
            } else {
              elm = range.document.createElement(tagName)
            }
            if (attrs) {
              domUtils.setAttributes(elm, attrs)
            }
            elm.appendChild(frag)
            range.insertNode(list ? top : elm)
            var aNode
            if (
              tagName == 'span' &&
              attrs.style &&
              /text\-decoration/.test(attrs.style) &&
              (aNode = domUtils.findParentByTagName(elm, 'a', true))
            ) {
              domUtils.setAttributes(aNode, attrs)
              domUtils.remove(elm, true)
              elm = aNode
            } else {
              domUtils.mergeSibling(elm)
              domUtils.clearEmptySibling(elm)
            }
            domUtils.mergeChild(elm, attrs)
            current = domUtils.getNextDomNode(elm, false, filterFn)
            domUtils.mergeToParent(elm)
            if (node === end) {
              break
            }
          } else {
            current = domUtils.getNextDomNode(current, true, filterFn)
          }
        }
        return this.moveToBookmark(bookmark)
      },
      removeInlineStyle: function(tagNames) {
        if (this.collapsed) {
          return this
        }
        tagNames = utils.isArray(tagNames) ? tagNames : [tagNames]
        this.shrinkBoundary().adjustmentBoundary()
        var start = this.startContainer
        var end = this.endContainer
        while (1) {
          if (start.nodeType == 1) {
            if (utils.indexOf(tagNames, start.tagName.toLowerCase()) > -1) {
              break
            }
            if (start.tagName.toLowerCase() == 'body') {
              start = null
              break
            }
          }
          start = start.parentNode
        }
        while (1) {
          if (end.nodeType == 1) {
            if (utils.indexOf(tagNames, end.tagName.toLowerCase()) > -1) {
              break
            }
            if (end.tagName.toLowerCase() == 'body') {
              end = null
              break
            }
          }
          end = end.parentNode
        }
        var bookmark = this.createBookmark()
        var frag
        var tmpRange
        if (start) {
          tmpRange = this.cloneRange()
            .setEndBefore(bookmark.start)
            .setStartBefore(start)
          frag = tmpRange.extractContents()
          tmpRange.insertNode(frag)
          domUtils.clearEmptySibling(start, true)
          start.parentNode.insertBefore(bookmark.start, start)
        }
        if (end) {
          tmpRange = this.cloneRange()
            .setStartAfter(bookmark.end)
            .setEndAfter(end)
          frag = tmpRange.extractContents()
          tmpRange.insertNode(frag)
          domUtils.clearEmptySibling(end, false, true)
          end.parentNode.insertBefore(bookmark.end, end.nextSibling)
        }
        var current = domUtils.getNextDomNode(bookmark.start, false, function(node) {
          return node.nodeType == 1
        })
        var next
        while (current && current !== bookmark.end) {
          next = domUtils.getNextDomNode(current, true, function(node) {
            return node.nodeType == 1
          })
          if (utils.indexOf(tagNames, current.tagName.toLowerCase()) > -1) {
            domUtils.remove(current, true)
          }
          current = next
        }
        return this.moveToBookmark(bookmark)
      },
      getClosedNode: function() {
        var node
        if (!this.collapsed) {
          var range = this.cloneRange()
            .adjustmentBoundary()
            .shrinkBoundary()
          if (selectOneNode(range)) {
            var child = range.startContainer.childNodes[range.startOffset]
            if (child && child.nodeType == 1 && (dtd.$empty[child.tagName] || dtd.$nonChild[child.tagName])) {
              node = child
            }
          }
        }
        return node
      },
      select: browser.ie
        ? function(noFillData, textRange) {
          var nativeRange
          if (!this.collapsed) {
            this.shrinkBoundary()
          }
          var node = this.getClosedNode()
          if (node && !textRange) {
            try {
              nativeRange = this.document.body.createControlRange()
              nativeRange.addElement(node)
              nativeRange.select()
            } catch (e) {
            }
            return this
          }
          var bookmark = this.createBookmark()
          var start = bookmark.start
          var end
          nativeRange = this.document.body.createTextRange()
          nativeRange.moveToElementText(start)
          nativeRange.moveStart('character', 1)
          if (!this.collapsed) {
            var nativeRangeEnd = this.document.body.createTextRange()
            end = bookmark.end
            nativeRangeEnd.moveToElementText(end)
            nativeRange.setEndPoint('EndToEnd', nativeRangeEnd)
          } else {
            if (!noFillData && this.startContainer.nodeType != 3) {
              var tmpText = this.document.createTextNode(fillChar)
              var tmp = this.document.createElement('span')
              tmp.appendChild(this.document.createTextNode(fillChar))
              start.parentNode.insertBefore(tmp, start)
              start.parentNode.insertBefore(tmpText, start)
              removeFillData(this.document, tmpText)
              fillData = tmpText
              mergeSibling(tmp, 'previousSibling')
              mergeSibling(start, 'nextSibling')
              nativeRange.moveStart('character', -1)
              nativeRange.collapse(true)
            }
          }
          this.moveToBookmark(bookmark)
          tmp && domUtils.remove(tmp)
          try {
            nativeRange.select()
          } catch (e) {
          }
          return this
        }
        : function(notInsertFillData) {
          function checkOffset(rng) {
            function check(node, offset, dir) {
              if (node.nodeType == 3 && node.nodeValue.length < offset) {
                rng[dir + 'Offset'] = node.nodeValue.length
              }
            }

            check(rng.startContainer, rng.startOffset, 'start')
            check(rng.endContainer, rng.endOffset, 'end')
          }

          var win = domUtils.getWindow(this.document)
          var sel = win.getSelection()
          var txtNode
          browser.gecko ? this.document.body.focus() : win.focus()
          if (sel) {
            sel.removeAllRanges()
            if (this.collapsed && !notInsertFillData) {
              var start = this.startContainer
              var child = start
              if (start.nodeType == 1) {
                child = start.childNodes[this.startOffset]
              }
              if (
                !(start.nodeType == 3 && this.startOffset) &&
                (child
                  ? !child.previousSibling || child.previousSibling.nodeType != 3
                  : !start.lastChild || start.lastChild.nodeType != 3)
              ) {
                txtNode = this.document.createTextNode(fillChar)
                this.insertNode(txtNode)
                removeFillData(this.document, txtNode)
                mergeSibling(txtNode, 'previousSibling')
                mergeSibling(txtNode, 'nextSibling')
                fillData = txtNode
                this.setStart(txtNode, browser.webkit ? 1 : 0).collapse(true)
              }
            }
            var nativeRange = this.document.createRange()
            if (this.collapsed && browser.opera && this.startContainer.nodeType == 1) {
              var child = this.startContainer.childNodes[this.startOffset]
              if (!child) {
                child = this.startContainer.lastChild
                if (child && domUtils.isBr(child)) {
                  this.setStartBefore(child).collapse(true)
                }
              } else {
                while (child && domUtils.isBlockElm(child)) {
                  if (child.nodeType == 1 && child.childNodes[0]) {
                    child = child.childNodes[0]
                  } else {
                    break
                  }
                }
                child && this.setStartBefore(child).collapse(true)
              }
            }
            checkOffset(this)
            nativeRange.setStart(this.startContainer, this.startOffset)
            nativeRange.setEnd(this.endContainer, this.endOffset)
            sel.addRange(nativeRange)
          }
          return this
        },
      scrollToView: function(win, offset) {
        win = win ? window : domUtils.getWindow(this.document)
        var me = this
        var span = me.document.createElement('span')
        span.innerHTML = '&nbsp;'
        me.cloneRange().insertNode(span)
        domUtils.scrollToView(span, win, offset)
        domUtils.remove(span)
        return me
      },
      inFillChar: function() {
        var start = this.startContainer
        if (
          this.collapsed &&
          start.nodeType == 3 &&
          start.nodeValue.replace(new RegExp('^' + domUtils.fillChar), '').length + 1 == start.nodeValue.length
        ) {
          return true
        }
        return false
      },
      createAddress: function(ignoreEnd, ignoreTxt) {
        var addr = {}
        var me = this

        function getAddress(isStart) {
          var node = isStart ? me.startContainer : me.endContainer
          var parents = domUtils.findParents(node, true, function(node) {
            return !domUtils.isBody(node)
          })
          var addrs = []
          for (var i = 0, ci; (ci = parents[i++]);) {
            addrs.push(domUtils.getNodeIndex(ci, ignoreTxt))
          }
          var firstIndex = 0
          if (ignoreTxt) {
            if (node.nodeType == 3) {
              var tmpNode = node.previousSibling
              while (tmpNode && tmpNode.nodeType == 3) {
                firstIndex += tmpNode.nodeValue.replace(fillCharReg, '').length
                tmpNode = tmpNode.previousSibling
              }
              firstIndex += isStart ? me.startOffset : me.endOffset
            } else {
              node = node.childNodes[isStart ? me.startOffset : me.endOffset]
              if (node) {
                firstIndex = domUtils.getNodeIndex(node, ignoreTxt)
              } else {
                node = isStart ? me.startContainer : me.endContainer
                var first = node.firstChild
                while (first) {
                  if (domUtils.isFillChar(first)) {
                    first = first.nextSibling
                    continue
                  }
                  firstIndex++
                  if (first.nodeType == 3) {
                    while (first && first.nodeType == 3) {
                      first = first.nextSibling
                    }
                  } else {
                    first = first.nextSibling
                  }
                }
              }
            }
          } else {
            firstIndex = isStart ? (domUtils.isFillChar(node) ? 0 : me.startOffset) : me.endOffset
          }
          if (firstIndex < 0) {
            firstIndex = 0
          }
          addrs.push(firstIndex)
          return addrs
        }

        addr.startAddress = getAddress(true)
        if (!ignoreEnd) {
          addr.endAddress = me.collapsed ? [].concat(addr.startAddress) : getAddress()
        }
        return addr
      },
      moveToAddress: function(addr, ignoreEnd) {
        var me = this

        function getNode(address, isStart) {
          var tmpNode = me.document.body
          var parentNode
          var offset
          for (var i = 0, ci, l = address.length; i < l; i++) {
            ci = address[i]
            parentNode = tmpNode
            tmpNode = tmpNode.childNodes[ci]
            if (!tmpNode) {
              offset = ci
              break
            }
          }
          if (isStart) {
            if (tmpNode) {
              me.setStartBefore(tmpNode)
            } else {
              me.setStart(parentNode, offset)
            }
          } else {
            if (tmpNode) {
              me.setEndBefore(tmpNode)
            } else {
              me.setEnd(parentNode, offset)
            }
          }
        }

        getNode(addr.startAddress, true)
        !ignoreEnd && addr.endAddress && getNode(addr.endAddress)
        return me
      },
      equals: function(rng) {
        for (var p in this) {
          if (this.hasOwnProperty(p)) {
            if (this[p] !== rng[p]) {
              return false
            }
          }
        }
        return true
      },
      traversal: function(doFn, filterFn) {
        if (this.collapsed) {
          return this
        }
        var bookmark = this.createBookmark()
        var end = bookmark.end
        var current = domUtils.getNextDomNode(bookmark.start, false, filterFn)
        while (current && current !== end && domUtils.getPosition(current, end) & domUtils.POSITION_PRECEDING) {
          var tmpNode = domUtils.getNextDomNode(current, false, filterFn)
          doFn(current)
          current = tmpNode
        }
        return this.moveToBookmark(bookmark)
      }
    }
  })()
  ;(function() {
    function getBoundaryInformation(range, start) {
      var getIndex = domUtils.getNodeIndex
      range = range.duplicate()
      range.collapse(start)
      var parent = range.parentElement()
      if (!parent.hasChildNodes()) {
        return { container: parent, offset: 0 }
      }
      var siblings = parent.children
      var child
      var testRange = range.duplicate()
      var startIndex = 0
      var endIndex = siblings.length - 1
      var index = -1
      var distance
      while (startIndex <= endIndex) {
        index = Math.floor((startIndex + endIndex) / 2)
        child = siblings[index]
        testRange.moveToElementText(child)
        var position = testRange.compareEndPoints('StartToStart', range)
        if (position > 0) {
          endIndex = index - 1
        } else {
          if (position < 0) {
            startIndex = index + 1
          } else {
            return { container: parent, offset: getIndex(child) }
          }
        }
      }
      if (index == -1) {
        testRange.moveToElementText(parent)
        testRange.setEndPoint('StartToStart', range)
        distance = testRange.text.replace(/(\r\n|\r)/g, '\n').length
        siblings = parent.childNodes
        if (!distance) {
          child = siblings[siblings.length - 1]
          return { container: child, offset: child.nodeValue.length }
        }
        var i = siblings.length
        while (distance > 0) {
          distance -= siblings[--i].nodeValue.length
        }
        return { container: siblings[i], offset: -distance }
      }
      testRange.collapse(position > 0)
      testRange.setEndPoint(position > 0 ? 'StartToStart' : 'EndToStart', range)
      distance = testRange.text.replace(/(\r\n|\r)/g, '\n').length
      if (!distance) {
        return dtd.$empty[child.tagName] || dtd.$nonChild[child.tagName]
          ? { container: parent, offset: getIndex(child) + (position > 0 ? 0 : 1) }
          : { container: child, offset: position > 0 ? 0 : child.childNodes.length }
      }
      while (distance > 0) {
        try {
          var pre = child
          child = child[position > 0 ? 'previousSibling' : 'nextSibling']
          distance -= child.nodeValue.length
        } catch (e) {
          return { container: parent, offset: getIndex(pre) }
        }
      }
      return { container: child, offset: position > 0 ? -distance : child.nodeValue.length + distance }
    }

    function transformIERangeToRange(ieRange, range) {
      if (ieRange.item) {
        range.selectNode(ieRange.item(0))
      } else {
        var bi = getBoundaryInformation(ieRange, true)
        range.setStart(bi.container, bi.offset)
        if (ieRange.compareEndPoints('StartToEnd', ieRange) != 0) {
          bi = getBoundaryInformation(ieRange, false)
          range.setEnd(bi.container, bi.offset)
        }
      }
      return range
    }

    function _getIERange(sel) {
      var ieRange
      try {
        ieRange = sel.getNative().createRange()
      } catch (e) {
        return null
      }
      var el = ieRange.item ? ieRange.item(0) : ieRange.parentElement()
      if ((el.ownerDocument || el) === sel.document) {
        return ieRange
      }
      return null
    }

    var Selection = (dom.Selection = function(doc) {
      var me = this
      var iframe
      me.document = doc
      if (browser.ie9below) {
        iframe = domUtils.getWindow(doc).frameElement
        domUtils.on(iframe, 'beforedeactivate', function() {
          me._bakIERange = me.getIERange()
        })
        domUtils.on(iframe, 'activate', function() {
          try {
            if (!_getIERange(me) && me._bakIERange) {
              me._bakIERange.select()
            }
          } catch (ex) {
          }
          me._bakIERange = null
        })
      }
      iframe = doc = null
    })
    Selection.prototype = {
      rangeInBody: function(rng, txtRange) {
        var node = browser.ie9below || txtRange ? (rng.item ? rng.item() : rng.parentElement()) : rng.startContainer
        return node === this.document.body || domUtils.inDoc(node, this.document)
      },
      getNative: function() {
        var doc = this.document
        try {
          return !doc ? null : browser.ie9below ? doc.selection : domUtils.getWindow(doc).getSelection()
        } catch (e) {
          return null
        }
      },
      getIERange: function() {
        var ieRange = _getIERange(this)
        if (!ieRange) {
          if (this._bakIERange) {
            return this._bakIERange
          }
        }
        return ieRange
      },
      cache: function() {
        this.clear()
        this._cachedRange = this.getRange()
        this._cachedStartElement = this.getStart()
        this._cachedStartElementPath = this.getStartElementPath()
      },
      getStartElementPath: function() {
        if (this._cachedStartElementPath) {
          return this._cachedStartElementPath
        }
        var start = this.getStart()
        if (start) {
          return domUtils.findParents(start, true, null, true)
        }
        return []
      },
      clear: function() {
        this._cachedStartElementPath = this._cachedRange = this._cachedStartElement = null
      },
      isFocus: function() {
        try {
          if (browser.ie9below) {
            var nativeRange = _getIERange(this)
            return !!(nativeRange && this.rangeInBody(nativeRange))
          } else {
            return !!this.getNative().rangeCount
          }
        } catch (e) {
          return false
        }
      },
      getRange: function() {
        var me = this

        function optimze(range) {
          var child = me.document.body.firstChild
          var collapsed = range.collapsed
          while (child && child.firstChild) {
            range.setStart(child, 0)
            child = child.firstChild
          }
          if (!range.startContainer) {
            range.setStart(me.document.body, 0)
          }
          if (collapsed) {
            range.collapse(true)
          }
        }

        if (me._cachedRange != null) {
          return this._cachedRange
        }
        var range = new baidu.editor.dom.Range(me.document)
        if (browser.ie9below) {
          var nativeRange = me.getIERange()
          if (nativeRange) {
            try {
              transformIERangeToRange(nativeRange, range)
            } catch (e) {
              optimze(range)
            }
          } else {
            optimze(range)
          }
        } else {
          var sel = me.getNative()
          if (sel && sel.rangeCount) {
            var firstRange = sel.getRangeAt(0)
            var lastRange = sel.getRangeAt(sel.rangeCount - 1)
            range
              .setStart(firstRange.startContainer, firstRange.startOffset)
              .setEnd(lastRange.endContainer, lastRange.endOffset)
            if (range.collapsed && domUtils.isBody(range.startContainer) && !range.startOffset) {
              optimze(range)
            }
          } else {
            if (this._bakRange && domUtils.inDoc(this._bakRange.startContainer, this.document)) {
              return this._bakRange
            }
            optimze(range)
          }
        }
        return (this._bakRange = range)
      },
      getStart: function() {
        if (this._cachedStartElement) {
          return this._cachedStartElement
        }
        var range = browser.ie9below ? this.getIERange() : this.getRange()
        var tmpRange
        var start
        var tmp
        var parent
        if (browser.ie9below) {
          if (!range) {
            return this.document.body.firstChild
          }
          if (range.item) {
            return range.item(0)
          }
          tmpRange = range.duplicate()
          tmpRange.text.length > 0 && tmpRange.moveStart('character', 1)
          tmpRange.collapse(1)
          start = tmpRange.parentElement()
          parent = tmp = range.parentElement()
          while ((tmp = tmp.parentNode)) {
            if (tmp == start) {
              start = parent
              break
            }
          }
        } else {
          range.shrinkBoundary()
          start = range.startContainer
          if (start.nodeType == 1 && start.hasChildNodes()) {
            start = start.childNodes[Math.min(start.childNodes.length - 1, range.startOffset)]
          }
          if (start.nodeType == 3) {
            return start.parentNode
          }
        }
        return start
      },
      getText: function() {
        var nativeSel, nativeRange
        if (this.isFocus() && (nativeSel = this.getNative())) {
          nativeRange = browser.ie9below ? nativeSel.createRange() : nativeSel.getRangeAt(0)
          return browser.ie9below ? nativeRange.text : nativeRange.toString()
        }
        return ''
      },
      clearRange: function() {
        this.getNative()[browser.ie9below ? 'empty' : 'removeAllRanges']()
      }
    }
  })()
  ;(function() {
    var uid = 0
    var _selectionChangeTimer

    function setValue(form, editor) {
      var textarea
      if (editor.textarea) {
        if (utils.isString(editor.textarea)) {
          for (var i = 0, ti, tis = domUtils.getElementsByTagName(form, 'textarea'); (ti = tis[i++]);) {
            if (ti.id == 'ueditor_textarea_' + editor.options.textarea) {
              textarea = ti
              break
            }
          }
        } else {
          textarea = editor.textarea
        }
      }
      if (!textarea) {
        form.appendChild(
          (textarea = domUtils.createElement(document, 'textarea', {
            name: editor.options.textarea,
            id: 'ueditor_textarea_' + editor.options.textarea,
            style: 'display:none'
          }))
        )
        editor.textarea = textarea
      }
      !textarea.getAttribute('name') && textarea.setAttribute('name', editor.options.textarea)
      textarea.value = editor.hasContents()
        ? editor.options.allHtmlEnabled
          ? editor.getAllHtml()
          : editor.getContent(null, null, true)
        : ''
    }

    function loadPlugins(me) {
      for (var pi in UE.plugins) {
        UE.plugins[pi].call(me)
      }
    }

    function checkCurLang(I18N) {
      for (var lang in I18N) {
        return lang
      }
    }

    function langReadied(me) {
      me.langIsReady = true
      me.fireEvent('langReady')
    }

    var Editor = (UE.Editor = function(options) {
      var me = this
      me.uid = uid++
      EventBase.call(me)
      me.commands = {}
      me.options = utils.extend(utils.clone(options || {}), UEDITOR_CONFIG, true)
      me.shortcutkeys = {}
      me.inputRules = []
      me.outputRules = []
      me.setOpt(Editor.defaultOptions(me))
      me.loadServerConfig()
      if (!utils.isEmptyObject(UE.I18N)) {
        me.options.lang = checkCurLang(UE.I18N)
        UE.plugin.load(me)
        langReadied(me)
      } else {
        utils.loadFile(
          document,
          {
            src: me.options.langPath + me.options.lang + '/' + me.options.lang + '.js',
            tag: 'script',
            type: 'text/javascript',
            defer: 'defer'
          },
          function() {
            UE.plugin.load(me)
            langReadied(me)
          }
        )
      }
      UE.instants['ueditorInstant' + me.uid] = me
    })
    Editor.prototype = {
      registerCommand: function(name, obj) {
        this.commands[name] = obj
      },
      ready: function(fn) {
        var me = this
        if (fn) {
          me.isReady ? fn.apply(me) : me.addListener('ready', fn)
        }
      },
      setOpt: function(key, val) {
        var obj = {}
        if (utils.isString(key)) {
          obj[key] = val
        } else {
          obj = key
        }
        utils.extend(this.options, obj, true)
      },
      getOpt: function(key) {
        return this.options[key]
      },
      destroy: function() {
        var me = this
        me.fireEvent('destroy')
        var container = me.container.parentNode
        var textarea = me.textarea
        if (!textarea) {
          textarea = document.createElement('textarea')
          container.parentNode.insertBefore(textarea, container)
        } else {
          textarea.style.display = ''
        }
        textarea.style.width = me.iframe.offsetWidth + 'px'
        textarea.style.height = me.iframe.offsetHeight + 'px'
        textarea.value = me.getContent()
        textarea.id = me.key
        container.innerHTML = ''
        domUtils.remove(container)
        var key = me.key
        for (var p in me) {
          if (me.hasOwnProperty(p)) {
            delete this[p]
          }
        }
        UE.delEditor(key)
      },
      render: function(container) {
        var me = this
        var options = me.options
        var getStyleValue = function(attr) {
          return parseInt(domUtils.getComputedStyle(container, attr))
        }
        if (utils.isString(container)) {
          container = document.getElementById(container)
        }
        if (container) {
          if (options.initialFrameWidth) {
            options.minFrameWidth = options.initialFrameWidth
          } else {
            options.minFrameWidth = options.initialFrameWidth = container.offsetWidth
          }
          if (options.initialFrameHeight) {
            options.minFrameHeight = options.initialFrameHeight
          } else {
            options.initialFrameHeight = options.minFrameHeight = container.offsetHeight
          }
          container.style.width = /%$/.test(options.initialFrameWidth)
            ? '100%'
            : options.initialFrameWidth - getStyleValue('padding-left') - getStyleValue('padding-right') + 'px'
          container.style.height = /%$/.test(options.initialFrameHeight)
            ? '100%'
            : options.initialFrameHeight - getStyleValue('padding-top') - getStyleValue('padding-bottom') + 'px'
          container.style.zIndex = options.zIndex
          var html =
            (ie && browser.version < 9 ? '' : '<!DOCTYPE html>') +
            '<html xmlns=\'http://www.w3.org/1999/xhtml\' class=\'view\' ><head>' +
            '<style type=\'text/css\'>' +
            '.view{padding:0;word-wrap:break-word;cursor:text;height:90%;}\n' +
            'body{margin:8px;font-family:sans-serif;font-size:18px;}' +
            'p{margin:5px 0;}</style>' +
            (options.iframeCssUrl
              ? '<link rel=\'stylesheet\' type=\'text/css\' href=\'' + utils.unhtml(options.iframeCssUrl) + '\'/>'
              : '') +
            (options.initialStyle ? '<style>' + options.initialStyle + '</style>' : '') +
            '</head><body class=\'view\' ></body>' +
            '<script type=\'text/javascript\' ' +
            (ie ? 'defer=\'defer\'' : '') +
            ' id=\'_initialScript\'>' +
            'setTimeout(function(){editor = window.parent.UE.instants[\'ueditorInstant' +
            me.uid +
            '\'];editor._setup(document);},0);' +
            'var _tmpScript = document.getElementById(\'_initialScript\');_tmpScript.parentNode.removeChild(_tmpScript);</script></html>'
          container.appendChild(
            domUtils.createElement(document, 'iframe', {
              id: 'ueditor_' + me.uid,
              width: '100%',
              height: '100%',
              frameborder: '0',
              src:
                'javascript:void(function(){document.open();' +
                (options.customDomain && document.domain != location.hostname
                  ? 'document.domain="' + document.domain + '";'
                  : '') +
                'document.write("' +
                html +
                '");document.close();}())'
            })
          )
          container.style.overflow = 'hidden'
          setTimeout(function() {
            if (/%$/.test(options.initialFrameWidth)) {
              options.minFrameWidth = options.initialFrameWidth = container.offsetWidth
            }
            if (/%$/.test(options.initialFrameHeight)) {
              options.minFrameHeight = options.initialFrameHeight = container.offsetHeight
              container.style.height = options.initialFrameHeight + 'px'
            }
          })
        }
      },
      _setup: function(doc) {
        var me = this
        var options = me.options
        if (ie) {
          doc.body.disabled = true
          doc.body.contentEditable = true
          doc.body.disabled = false
        } else {
          doc.body.contentEditable = true
        }
        doc.body.spellcheck = false
        me.document = doc
        me.window = doc.defaultView || doc.parentWindow
        me.iframe = me.window.frameElement
        me.body = doc.body
        me.selection = new dom.Selection(doc)
        var geckoSel
        if (browser.gecko && (geckoSel = this.selection.getNative())) {
          geckoSel.removeAllRanges()
        }
        this._initEvents()
        for (var form = this.iframe.parentNode; !domUtils.isBody(form); form = form.parentNode) {
          if (form.tagName == 'FORM') {
            me.form = form
            if (me.options.autoSyncData) {
              domUtils.on(me.window, 'blur', function() {
                setValue(form, me)
              })
            } else {
              domUtils.on(form, 'submit', function() {
                setValue(this, me)
              })
            }
            break
          }
        }
        if (options.initialContent) {
          if (options.autoClearinitialContent) {
            var oldExecCommand = me.execCommand
            me.execCommand = function() {
              me.fireEvent('firstBeforeExecCommand')
              return oldExecCommand.apply(me, arguments)
            }
            this._setDefaultContent(options.initialContent)
          } else {
            this.setContent(options.initialContent, false, true)
          }
        }
        if (domUtils.isEmptyNode(me.body)) {
          me.body.innerHTML = '<p style="font-size:18px">' + (browser.ie ? '' : '<br/>') + '</p>'
        }
        if (options.focus) {
          setTimeout(function() {
            me.focus(me.options.focusInEnd)
            !me.options.autoClearinitialContent && me._selectionChange()
          }, 0)
        }
        if (!me.container) {
          me.container = this.iframe.parentNode
        }
        if (options.fullscreen && me.ui) {
          me.ui.setFullScreen(true)
        }
        try {
          me.document.execCommand('2D-position', false, false)
        } catch (e) {
        }
        try {
          me.document.execCommand('enableInlineTableEditing', false, false)
        } catch (e) {
        }
        try {
          me.document.execCommand('enableObjectResizing', false, false)
        } catch (e) {
        }
        me._bindshortcutKeys()
        me.isReady = 1
        me.fireEvent('ready')
        options.onready && options.onready.call(me)
        if (!browser.ie9below) {
          domUtils.on(me.window, ['blur', 'focus'], function(e) {
            if (e.type == 'blur') {
              me._bakRange = me.selection.getRange()
              try {
                me._bakNativeRange = me.selection.getNative().getRangeAt(0)
                me.selection.getNative().removeAllRanges()
              } catch (e) {
                me._bakNativeRange = null
              }
            } else {
              try {
                me._bakRange && me._bakRange.select()
              } catch (e) {
              }
            }
          })
        }
        if (browser.gecko && browser.version <= 10902) {
          me.body.contentEditable = false
          setTimeout(function() {
            me.body.contentEditable = true
          }, 100)
          setInterval(function() {
            me.body.style.height = me.iframe.offsetHeight - 20 + 'px'
          }, 100)
        }
        !options.isShow && me.setHide()
        options.readonly && me.setDisabled()
      },
      sync: function(formId) {
        var me = this
        var form = formId
          ? document.getElementById(formId)
          : domUtils.findParent(
            me.iframe.parentNode,
            function(node) {
              return node.tagName == 'FORM'
            },
            true
          )
        form && setValue(form, me)
      },
      setHeight: function(height, notSetHeight) {
        if (height !== parseInt(this.iframe.parentNode.style.height)) {
          this.iframe.parentNode.style.height = height + 'px'
        }
        !notSetHeight && (this.options.minFrameHeight = this.options.initialFrameHeight = height)
        this.body.style.height = height + 'px'
        !notSetHeight && this.trigger('setHeight')
      },
      addshortcutkey: function(cmd, keys) {
        var obj = {}
        if (keys) {
          obj[cmd] = keys
        } else {
          obj = cmd
        }
        utils.extend(this.shortcutkeys, obj)
      },
      _bindshortcutKeys: function() {
        var me = this
        var shortcutkeys = this.shortcutkeys
        me.addListener('keydown', function(type, e) {
          var keyCode = e.keyCode || e.which
          for (var i in shortcutkeys) {
            var tmp = shortcutkeys[i].split(',')
            for (var t = 0, ti; (ti = tmp[t++]);) {
              ti = ti.split(':')
              var key = ti[0]
              var param = ti[1]
              if (/^(ctrl)(\+shift)?\+(\d+)$/.test(key.toLowerCase()) || /^(\d+)$/.test(key)) {
                if (
                  ((RegExp.$1 == 'ctrl' ? e.ctrlKey || e.metaKey : 0) &&
                    (RegExp.$2 != '' ? e[RegExp.$2.slice(1) + 'Key'] : 1) &&
                    keyCode == RegExp.$3) ||
                  keyCode == RegExp.$1
                ) {
                  if (me.queryCommandState(i, param) != -1) {
                    me.execCommand(i, param)
                  }
                  domUtils.preventDefault(e)
                }
              }
            }
          }
        })
      },
      getContent: function(cmd, fn, notSetCursor, ignoreBlank, formatter) {
        var me = this
        if (cmd && utils.isFunction(cmd)) {
          fn = cmd
          cmd = ''
        }
        if (fn ? !fn() : !this.hasContents()) {
          return ''
        }
        me.fireEvent('beforegetcontent')
        var root = UE.htmlparser(me.body.innerHTML, ignoreBlank)
        me.filterOutputRule(root)
        me.fireEvent('aftergetcontent', cmd, root)
        return root.toHtml(formatter)
      },
      getAllHtml: function() {
        var me = this
        var headHtml = []
        var html = ''
        me.fireEvent('getAllHtml', headHtml)
        if (browser.ie && browser.version > 8) {
          var headHtmlForIE9 = ''
          utils.each(me.document.styleSheets, function(si) {
            headHtmlForIE9 += si.href
              ? '<link rel="stylesheet" type="text/css" href="' + si.href + '" />'
              : '<style>' + si.cssText + '</style>'
          })
          utils.each(me.document.getElementsByTagName('script'), function(si) {
            headHtmlForIE9 += si.outerHTML
          })
        }
        return (
          '<html><head>' +
          (me.options.charset
            ? '<meta http-equiv="Content-Type" content="text/html; charset=' + me.options.charset + '"/>'
            : '') +
          (headHtmlForIE9 || me.document.getElementsByTagName('head')[0].innerHTML) +
          headHtml.join('\n') +
          '</head>' +
          '<body ' +
          (ie && browser.version < 9 ? 'class="view"' : '') +
          '>' +
          me.getContent(null, null, true) +
          '</body></html>'
        )
      },
      getPlainTxt: function() {
        var reg = new RegExp(domUtils.fillChar, 'g')
        var html = this.body.innerHTML.replace(/[\n\r]/g, '')
        html = html
          .replace(/<(p|div)[^>]*>(<br\/?>|&nbsp;)<\/\1>/gi, '\n')
          .replace(/<br\/?>/gi, '\n')
          .replace(/<[^>/]+>/g, '')
          .replace(/(\n)?<\/([^>]+)>/g, function(a, b, c) {
            return dtd.$block[c] ? '\n' : b || ''
          })
        return html
          .replace(reg, '')
          .replace(/\u00a0/g, ' ')
          .replace(/&nbsp;/g, ' ')
      },
      getContentTxt: function() {
        var reg = new RegExp(domUtils.fillChar, 'g')
        return this.body[browser.ie ? 'innerText' : 'textContent'].replace(reg, '').replace(/\u00a0/g, ' ')
      },
      setContent: function(html, isAppendTo, notFireSelectionchange) {
        var me = this
        me.body.innerHTML = (isAppendTo ? me.body.innerHTML : '') + html

        function isCdataDiv(node) {
          return node.tagName == 'DIV' && node.getAttribute('cdata_tag')
        }

        if (me.options.enterTag == 'p') {
          var child = this.body.firstChild
          var tmpNode
          if (
            !child ||
            (child.nodeType == 1 &&
              (dtd.$cdata[child.tagName] || isCdataDiv(child) || domUtils.isCustomeNode(child)) &&
              child === this.body.lastChild)
          ) {
            this.body.innerHTML = '<p style="font-size:18px">' + (browser.ie ? '&nbsp;' : '<br/>') + '</p>' + this.body.innerHTML
          } else {
            var p = me.document.createElement('p')
            while (child) {
              while (
                child &&
                (child.nodeType == 3 || (child.nodeType == 1 && dtd.p[child.tagName] && !dtd.$cdata[child.tagName]))
                ) {
                tmpNode = child.nextSibling
                p.appendChild(child)
                child = tmpNode
              }
              if (p.firstChild) {
                if (!child) {
                  me.body.appendChild(p)
                  break
                } else {
                  child.parentNode.insertBefore(p, child)
                  p = me.document.createElement('p')
                }
              }
              child = child.nextSibling
            }
          }
        }
        me.fireEvent('aftersetcontent')
        me.fireEvent('contentchange')
        !notFireSelectionchange && me._selectionChange()
        me._bakRange = me._bakIERange = me._bakNativeRange = null
        var geckoSel
        if (browser.gecko && (geckoSel = this.selection.getNative())) {
          geckoSel.removeAllRanges()
        }
        if (me.options.autoSyncData) {
          me.form && setValue(me.form, me)
        }
      },
      focus: function(toEnd) {
        try {
          var me = this
          var rng = me.selection.getRange()
          if (toEnd) {
            var node = me.body.lastChild
            if (node && node.nodeType == 1 && !dtd.$empty[node.tagName]) {
              if (domUtils.isEmptyBlock(node)) {
                rng.setStartAtFirst(node)
              } else {
                rng.setStartAtLast(node)
              }
              rng.collapse(true)
            }
            rng.setCursor(true)
          } else {
            if (!rng.collapsed && domUtils.isBody(rng.startContainer) && rng.startOffset == 0) {
              var node = me.body.firstChild
              if (node && node.nodeType == 1 && !dtd.$empty[node.tagName]) {
                rng.setStartAtFirst(node).collapse(true)
              }
            }
            rng.select(true)
          }
          this.fireEvent('focus selectionchange')
        } catch (e) {
        }
      },
      isFocus: function() {
        return this.selection.isFocus()
      },
      blur: function() {
        var sel = this.selection.getNative()
        if (sel.empty && browser.ie) {
          var nativeRng = document.body.createTextRange()
          nativeRng.moveToElementText(document.body)
          nativeRng.collapse(true)
          nativeRng.select()
          sel.empty()
        } else {
          sel.removeAllRanges()
        }
      },
      _initEvents: function() {
        var me = this
        var doc = me.document
        var win = me.window
        me._proxyDomEvent = utils.bind(me._proxyDomEvent, me)
        domUtils.on(
          doc,
          [
            'click',
            'contextmenu',
            'mousedown',
            'keydown',
            'keyup',
            'keypress',
            'mouseup',
            'mouseover',
            'mouseout',
            'selectstart'
          ],
          me._proxyDomEvent
        )
        domUtils.on(win, ['focus', 'blur'], me._proxyDomEvent)
        domUtils.on(me.body, 'drop', function(e) {
          if (browser.gecko && e.stopPropagation) {
            e.stopPropagation()
          }
          me.fireEvent('contentchange')
        })
        domUtils.on(doc, ['mouseup', 'keydown'], function(evt) {
          if (evt.type == 'keydown' && (evt.ctrlKey || evt.metaKey || evt.shiftKey || evt.altKey)) {
            return
          }
          if (evt.button == 2) {
            return
          }
          me._selectionChange(250, evt)
        })
      },
      _proxyDomEvent: function(evt) {
        if (this.fireEvent('before' + evt.type.replace(/^on/, '').toLowerCase()) === false) {
          return false
        }
        if (this.fireEvent(evt.type.replace(/^on/, ''), evt) === false) {
          return false
        }
        return this.fireEvent('after' + evt.type.replace(/^on/, '').toLowerCase())
      },
      _selectionChange: function(delay, evt) {
        var me = this
        var hackForMouseUp = false
        var mouseX, mouseY
        if (browser.ie && browser.version < 9 && evt && evt.type == 'mouseup') {
          var range = this.selection.getRange()
          if (!range.collapsed) {
            hackForMouseUp = true
            mouseX = evt.clientX
            mouseY = evt.clientY
          }
        }
        clearTimeout(_selectionChangeTimer)
        _selectionChangeTimer = setTimeout(function() {
          if (!me.selection || !me.selection.getNative()) {
            return
          }
          var ieRange
          if (hackForMouseUp && me.selection.getNative().type == 'None') {
            ieRange = me.document.body.createTextRange()
            try {
              ieRange.moveToPoint(mouseX, mouseY)
            } catch (ex) {
              ieRange = null
            }
          }
          var bakGetIERange
          if (ieRange) {
            bakGetIERange = me.selection.getIERange
            me.selection.getIERange = function() {
              return ieRange
            }
          }
          me.selection.cache()
          if (bakGetIERange) {
            me.selection.getIERange = bakGetIERange
          }
          if (me.selection._cachedRange && me.selection._cachedStartElement) {
            me.fireEvent('beforeselectionchange')
            me.fireEvent('selectionchange', !!evt)
            me.fireEvent('afterselectionchange')
            me.selection.clear()
          }
        }, delay || 50)
      },
      _callCmdFn: function(fnName, args) {
        var cmdName = args[0].toLowerCase()
        var cmd
        var cmdFn
        cmd = this.commands[cmdName] || UE.commands[cmdName]
        cmdFn = cmd && cmd[fnName]
        if ((!cmd || !cmdFn) && fnName == 'queryCommandState') {
          return 0
        } else {
          if (cmdFn) {
            return cmdFn.apply(this, args)
          }
        }
      },
      execCommand: function(cmdName) {
        cmdName = cmdName.toLowerCase()
        var me = this
        var result
        var cmd = me.commands[cmdName] || UE.commands[cmdName]
        if (!cmd || !cmd.execCommand) {
          return null
        }
        if (!cmd.notNeedUndo && !me.__hasEnterExecCommand) {
          me.__hasEnterExecCommand = true
          if (me.queryCommandState.apply(me, arguments) != -1) {
            me.fireEvent('saveScene')
            me.fireEvent.apply(me, ['beforeexeccommand', cmdName].concat(arguments))
            result = this._callCmdFn('execCommand', arguments)
            me.fireEvent.apply(me, ['afterexeccommand', cmdName].concat(arguments))
            me.fireEvent('saveScene')
          }
          me.__hasEnterExecCommand = false
        } else {
          result = this._callCmdFn('execCommand', arguments)
          !me.__hasEnterExecCommand &&
          !cmd.ignoreContentChange &&
          !me._ignoreContentChange &&
          me.fireEvent('contentchange')
        }
        !me.__hasEnterExecCommand && !cmd.ignoreContentChange && !me._ignoreContentChange && me._selectionChange()
        return result
      },
      queryCommandState: function(cmdName) {
        return this._callCmdFn('queryCommandState', arguments)
      },
      queryCommandValue: function(cmdName) {
        return this._callCmdFn('queryCommandValue', arguments)
      },
      hasContents: function(tags) {
        if (tags) {
          for (var i = 0, ci; (ci = tags[i++]);) {
            if (this.document.getElementsByTagName(ci).length > 0) {
              return true
            }
          }
        }
        if (!domUtils.isEmptyBlock(this.body)) {
          return true
        }
        tags = ['div']
        for (i = 0; (ci = tags[i++]);) {
          var nodes = domUtils.getElementsByTagName(this.document, ci)
          for (var n = 0, cn; (cn = nodes[n++]);) {
            if (domUtils.isCustomeNode(cn)) {
              return true
            }
          }
        }
        return false
      },
      reset: function() {
        this.fireEvent('reset')
      },
      setEnabled: function() {
        var me = this
        var range
        if (me.body.contentEditable == 'false') {
          me.body.contentEditable = true
          range = me.selection.getRange()
          try {
            range.moveToBookmark(me.lastBk)
            delete me.lastBk
          } catch (e) {
            range.setStartAtFirst(me.body).collapse(true)
          }
          range.select(true)
          if (me.bkqueryCommandState) {
            me.queryCommandState = me.bkqueryCommandState
            delete me.bkqueryCommandState
          }
          if (me.bkqueryCommandValue) {
            me.queryCommandValue = me.bkqueryCommandValue
            delete me.bkqueryCommandValue
          }
          me.fireEvent('selectionchange')
        }
      },
      enable: function() {
        return this.setEnabled()
      },
      setDisabled: function(except) {
        var me = this
        except = except ? (utils.isArray(except) ? except : [except]) : []
        if (me.body.contentEditable == 'true') {
          if (!me.lastBk) {
            me.lastBk = me.selection.getRange().createBookmark(true)
          }
          me.body.contentEditable = false
          me.bkqueryCommandState = me.queryCommandState
          me.bkqueryCommandValue = me.queryCommandValue
          me.queryCommandState = function(type) {
            if (utils.indexOf(except, type) != -1) {
              return me.bkqueryCommandState.apply(me, arguments)
            }
            return -1
          }
          me.queryCommandValue = function(type) {
            if (utils.indexOf(except, type) != -1) {
              return me.bkqueryCommandValue.apply(me, arguments)
            }
            return null
          }
          me.fireEvent('selectionchange')
        }
      },
      disable: function(except) {
        return this.setDisabled(except)
      },
      _setDefaultContent: (function() {
        function clear() {
          var me = this
          if (me.document.getElementById('initContent')) {
            me.body.innerHTML = '<p style="font-size:18px">' + (ie ? '' : '<br/>') + '</p>'
            me.removeListener('firstBeforeExecCommand focus', clear)
            setTimeout(function() {
              me.focus()
              me._selectionChange()
            }, 0)
          }
        }

        return function(cont) {
          var me = this
          me.body.innerHTML = '<p id="initContent">' + cont + '</p>'
          me.addListener('firstBeforeExecCommand focus', clear)
        }
      })(),
      setShow: function() {
        var me = this
        var range = me.selection.getRange()
        if (me.container.style.display == 'none') {
          try {
            range.moveToBookmark(me.lastBk)
            delete me.lastBk
          } catch (e) {
            range.setStartAtFirst(me.body).collapse(true)
          }
          setTimeout(function() {
            range.select(true)
          }, 100)
          me.container.style.display = ''
        }
      },
      show: function() {
        return this.setShow()
      },
      setHide: function() {
        var me = this
        if (!me.lastBk) {
          me.lastBk = me.selection.getRange().createBookmark(true)
        }
        me.container.style.display = 'none'
      },
      hide: function() {
        return this.setHide()
      },
      getLang: function(path) {
        var lang = UE.I18N[this.options.lang]
        if (!lang) {
          throw Error('not import language file')
        }
        path = (path || '').split('.')
        for (var i = 0, ci; (ci = path[i++]);) {
          lang = lang[ci]
          if (!lang) {
            break
          }
        }
        return lang
      },
      getContentLength: function(ingoneHtml, tagNames) {
        var count = this.getContent(false, false, true).length
        if (ingoneHtml) {
          tagNames = (tagNames || []).concat(['hr', 'img', 'iframe'])
          count = this.getContentTxt().replace(/[\t\r\n]+/g, '').length
          for (var i = 0, ci; (ci = tagNames[i++]);) {
            count += this.document.getElementsByTagName(ci).length
          }
        }
        return count
      },
      addInputRule: function(rule) {
        this.inputRules.push(rule)
      },
      filterInputRule: function(root) {
        for (var i = 0, ci; (ci = this.inputRules[i++]);) {
          ci.call(this, root)
        }
      },
      addOutputRule: function(rule) {
        this.outputRules.push(rule)
      },
      filterOutputRule: function(root) {
        for (var i = 0, ci; (ci = this.outputRules[i++]);) {
          ci.call(this, root)
        }
      },
      getActionUrl: function(action) {
        var actionName = this.getOpt(action) || action
        var imageUrl = this.getOpt('imageUrl')
        var serverUrl = this.getOpt('serverUrl')
        if (!serverUrl && imageUrl) {
          serverUrl = imageUrl.replace(/^(.*[\/]).+([\.].+)$/, '$1controller$2')
        }
        if (serverUrl) {
          serverUrl = serverUrl + (serverUrl.indexOf('?') == -1 ? '?' : '&') + 'action=' + (actionName || '')
          return utils.formatUrl(serverUrl)
        } else {
          return ''
        }
      }
    }
    utils.inherits(Editor, EventBase)
  })()
  UE.Editor.defaultOptions = function(editor) {
    var _url = editor.options.UEDITOR_HOME_URL
    return {
      isShow: true,
      initialContent: '',
      initialStyle: '',
      autoClearinitialContent: false,
      iframeCssUrl: _url + 'themes/iframe.css',
      textarea: 'editorValue',
      focus: false,
      focusInEnd: true,
      autoClearEmptyNode: true,
      fullscreen: false,
      readonly: false,
      zIndex: 999,
      imagePopup: true,
      enterTag: 'p',
      customDomain: false,
      lang: 'zh-cn',
      langPath: _url + 'lang/',
      theme: 'default',
      themePath: _url + 'themes/',
      allHtmlEnabled: false,
      scaleEnabled: false,
      tableNativeEditInFF: false,
      autoSyncData: true,
      fileNameFormat: '{time}{rand:6}'
    }
  }
  ;(function() {
    UE.Editor.prototype.loadServerConfig = function() {
      var me = this
      setTimeout(function() {
        try {
          me.options.imageUrl &&
          me.setOpt('serverUrl', me.options.imageUrl.replace(/^(.*[\/]).+([\.].+)$/, '$1controller$2'))
          var configUrl = me.getActionUrl('config')
          var isJsonp = utils.isCrossDomainUrl(configUrl)
          me._serverConfigLoaded = false
          configUrl &&
          UE.ajax.request(configUrl, {
            method: 'GET',
            dataType: isJsonp ? 'jsonp' : '',
            onsuccess: function(r) {
              try {
                var config = isJsonp ? r : eval('(' + r.responseText + ')')
                utils.extend(me.options, config)
                me.fireEvent('serverConfigLoaded')
                me._serverConfigLoaded = true
              } catch (e) {
                showErrorMsg(me.getLang('loadconfigFormatError'))
              }
            },
            onerror: function() {
              showErrorMsg(me.getLang('loadconfigHttpError'))
            }
          })
        } catch (e) {
          showErrorMsg(me.getLang('loadconfigError'))
        }
      })

      function showErrorMsg(msg) {
        console && console.error(msg)
      }
    }
    UE.Editor.prototype.isServerConfigLoaded = function() {
      var me = this
      return me._serverConfigLoaded || false
    }
    UE.Editor.prototype.afterConfigReady = function(handler) {
      if (!handler || !utils.isFunction(handler)) {
        return
      }
      var me = this
      var readyHandler = function() {
        handler.apply(me, arguments)
        me.removeListener('serverConfigLoaded', readyHandler)
      }
      if (me.isServerConfigLoaded()) {
        handler.call(me, 'serverConfigLoaded')
      } else {
        me.addListener('serverConfigLoaded', readyHandler)
      }
    }
  })()
  UE.ajax = (function() {
    var fnStr = 'XMLHttpRequest()'
    try {
      new ActiveXObject('Msxml2.XMLHTTP')
      fnStr = 'ActiveXObject(\'Msxml2.XMLHTTP\')'
    } catch (e) {
      try {
        new ActiveXObject('Microsoft.XMLHTTP')
        fnStr = 'ActiveXObject(\'Microsoft.XMLHTTP\')'
      } catch (e) {
      }
    }
    var creatAjaxRequest = new Function('return new ' + fnStr)

    function json2str(json) {
      var strArr = []
      for (var i in json) {
        if (i == 'method' || i == 'timeout' || i == 'async' || i == 'dataType' || i == 'callback') {
          continue
        }
        if (json[i] == undefined || json[i] == null) {
          continue
        }
        if (!((typeof json[i]).toLowerCase() == 'function' || (typeof json[i]).toLowerCase() == 'object')) {
          strArr.push(encodeURIComponent(i) + '=' + encodeURIComponent(json[i]))
        } else {
          if (utils.isArray(json[i])) {
            for (var j = 0; j < json[i].length; j++) {
              strArr.push(encodeURIComponent(i) + '[]=' + encodeURIComponent(json[i][j]))
            }
          }
        }
      }
      return strArr.join('&')
    }

    function doAjax(url, ajaxOptions) {
      var xhr = creatAjaxRequest()
      var timeIsOut = false
      var defaultAjaxOptions = {
        method: 'POST',
        timeout: 5000,
        async: true,
        data: {},
        onsuccess: function() {
        },
        onerror: function() {
        }
      }
      if (typeof url === 'object') {
        ajaxOptions = url
        url = ajaxOptions.url
      }
      if (!xhr || !url) {
        return
      }
      var ajaxOpts = ajaxOptions ? utils.extend(defaultAjaxOptions, ajaxOptions) : defaultAjaxOptions
      var submitStr = json2str(ajaxOpts)
      if (!utils.isEmptyObject(ajaxOpts.data)) {
        submitStr += (submitStr ? '&' : '') + json2str(ajaxOpts.data)
      }
      var timerID = setTimeout(function() {
        if (xhr.readyState != 4) {
          timeIsOut = true
          xhr.abort()
          clearTimeout(timerID)
        }
      }, ajaxOpts.timeout)
      var method = ajaxOpts.method.toUpperCase()
      var str =
        url + (url.indexOf('?') == -1 ? '?' : '&') + (method == 'POST' ? '' : submitStr + '&noCache=' + +new Date())
      xhr.open(method, str, ajaxOpts.async)
      xhr.onreadystatechange = function() {
        if (xhr.readyState == 4) {
          if (!timeIsOut && xhr.status == 200) {
            ajaxOpts.onsuccess(xhr)
          } else {
            ajaxOpts.onerror(xhr)
          }
        }
      }
      if (method == 'POST') {
        xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded')
        xhr.send(submitStr)
      } else {
        xhr.send(null)
      }
    }

    function doJsonp(url, opts) {
      var successhandler = opts.onsuccess || function() {
      }
      var scr = document.createElement('SCRIPT')
      var options = opts || {}
      var charset = options['charset']
      var callbackField = options['jsonp'] || 'callback'
      var callbackFnName
      var timeOut = options['timeOut'] || 0
      var timer
      var reg = new RegExp('(\\?|&)' + callbackField + '=([^&]*)')
      var matches
      if (utils.isFunction(successhandler)) {
        callbackFnName = 'bd__editor__' + Math.floor(Math.random() * 2147483648).toString(36)
        window[callbackFnName] = getCallBack(0)
      } else {
        if (utils.isString(successhandler)) {
          callbackFnName = successhandler
        } else {
          if ((matches = reg.exec(url))) {
            callbackFnName = matches[2]
          }
        }
      }
      url = url.replace(reg, '\x241' + callbackField + '=' + callbackFnName)
      if (url.search(reg) < 0) {
        url += (url.indexOf('?') < 0 ? '?' : '&') + callbackField + '=' + callbackFnName
      }
      var queryStr = json2str(opts)
      if (!utils.isEmptyObject(opts.data)) {
        queryStr += (queryStr ? '&' : '') + json2str(opts.data)
      }
      if (queryStr) {
        url = url.replace(/\?/, '?' + queryStr + '&')
      }
      scr.onerror = getCallBack(1)
      if (timeOut) {
        timer = setTimeout(getCallBack(1), timeOut)
      }
      createScriptTag(scr, url, charset)

      function createScriptTag(scr, url, charset) {
        scr.setAttribute('type', 'text/javascript')
        scr.setAttribute('defer', 'defer')
        charset && scr.setAttribute('charset', charset)
        scr.setAttribute('src', url)
        document.getElementsByTagName('head')[0].appendChild(scr)
      }

      function getCallBack(onTimeOut) {
        return function() {
          try {
            if (onTimeOut) {
              options.onerror && options.onerror()
            } else {
              try {
                clearTimeout(timer)
                successhandler.apply(window, arguments)
              } catch (e) {
              }
            }
          } catch (exception) {
            options.onerror && options.onerror.call(window, exception)
          } finally {
            options.oncomplete && options.oncomplete.apply(window, arguments)
            scr.parentNode && scr.parentNode.removeChild(scr)
            window[callbackFnName] = null
            try {
              delete window[callbackFnName]
            } catch (e) {
            }
          }
        }
      }
    }

    return {
      request: function(url, opts) {
        if (opts && opts.dataType == 'jsonp') {
          doJsonp(url, opts)
        } else {
          doAjax(url, opts)
        }
      },
      getJSONP: function(url, data, fn) {
        var opts = { data: data, oncomplete: fn }
        doJsonp(url, opts)
      }
    }
  })()
  var filterWord = (UE.filterWord = (function() {
      function isWordDocument(str) {
        return /(class="?Mso|style="[^"]*\bmso\-|w:WordDocument|<(v|o):|lang=)/gi.test(str)
      }

      function transUnit(v) {
        v = v.replace(/[\d.]+\w+/g, function(m) {
          return utils.transUnitToPx(m)
        })
        return v
      }

      function filterPasteWord(str) {
        return str
          .replace(/[\t\r\n]+/g, ' ')
          .replace(/<!--[\s\S]*?-->/gi, '')
          .replace(/<v:shape [^>]*>[\s\S]*?.<\/v:shape>/gi, function(str) {
            if (browser.opera) {
              return ''
            }
            try {
              if (/Bitmap/i.test(str)) {
                return ''
              }
              var width = str.match(/width:([ \d.]*p[tx])/i)[1]
              var height = str.match(/height:([ \d.]*p[tx])/i)[1]
              var src = str.match(/src=\s*"([^"]*)"/i)[1]
              return '<img width="' + transUnit(width) + '" height="' + transUnit(height) + '" src="' + src + '" />'
            } catch (e) {
              return ''
            }
          })
          .replace(/<\/?div[^>]*>/g, '')
          .replace(/v:\w+=(["']?)[^'"]+\1/g, '')
          .replace(
            /<(!|script[^>]*>.*?<\/script(?=[>\s])|\/?(\?xml(:\w+)?|xml|meta|link|style|\w+:\w+)(?=[\s\/>]))[^>]*>/gi,
            ''
          )
          .replace(/<p [^>]*class="?MsoHeading"?[^>]*>(.*?)<\/p>/gi, '<p><strong>$1</strong></p>')
          .replace(/\s+(class|lang|align)\s*=\s*(['"]?)([\w-]+)\2/gi, function(str, name, marks, val) {
            return name == 'class' && val == 'MsoListParagraph' ? str : ''
          })
          .replace(/<(font|span)[^>]*>(\s*)<\/\1>/gi, function(a, b, c) {
            return c.replace(/[\t\r\n ]+/g, ' ')
          })
          .replace(/(<[a-z][^>]*)\sstyle=(["'])([^\2]*?)\2/gi, function(str, tag, tmp, style) {
            var n = []
            var s = style
              .replace(/^\s+|\s+$/, '')
              .replace(/&#39;/g, '\'')
              .replace(/&quot;/gi, '\'')
              .replace(/[\d.]+(cm|pt)/g, function(str) {
                return utils.transUnitToPx(str)
              })
              .split(/;\s*/g)
            for (var i = 0, v; (v = s[i]); i++) {
              var name
              var value
              var parts = v.split(':')
              if (parts.length == 2) {
                name = parts[0].toLowerCase()
                value = parts[1].toLowerCase()
                if (
                  (/^(background)\w*/.test(name) && value.replace(/(initial|\s)/g, '').length == 0) ||
                  (/^(margin)\w*/.test(name) && /^0\w+$/.test(value))
                ) {
                  continue
                }
                switch (name) {
                  case 'mso-padding-alt':
                  case 'mso-padding-top-alt':
                  case 'mso-padding-right-alt':
                  case 'mso-padding-bottom-alt':
                  case 'mso-padding-left-alt':
                  case 'mso-margin-alt':
                  case 'mso-margin-top-alt':
                  case 'mso-margin-right-alt':
                  case 'mso-margin-bottom-alt':
                  case 'mso-margin-left-alt':
                  case 'mso-height':
                  case 'mso-width':
                  case 'mso-vertical-align-alt':
                    if (!/<table/.test(tag)) {
                      n[i] = name.replace(/^mso-|-alt$/g, '') + ':' + transUnit(value)
                    }
                    continue
                  case 'horiz-align':
                    n[i] = 'text-align:' + value
                    continue
                  case 'vert-align':
                    n[i] = 'vertical-align:' + value
                    continue
                  case 'font-color':
                  case 'mso-foreground':
                    n[i] = 'color:' + value
                    continue
                  case 'mso-background':
                  case 'mso-highlight':
                    n[i] = 'background:' + value
                    continue
                  case 'mso-default-height':
                    n[i] = 'min-height:' + transUnit(value)
                    continue
                  case 'mso-default-width':
                    n[i] = 'min-width:' + transUnit(value)
                    continue
                  case 'mso-padding-between-alt':
                    n[i] = 'border-collapse:separate;border-spacing:' + transUnit(value)
                    continue
                  case 'text-line-through':
                    if (value == 'single' || value == 'double') {
                      n[i] = 'text-decoration:line-through'
                    }
                    continue
                  case 'mso-zero-height':
                    if (value == 'yes') {
                      n[i] = 'display:none'
                    }
                    continue
                  case 'margin':
                    if (!/[1-9]/.test(value)) {
                      continue
                    }
                }
                if (
                  /^(mso|column|font-emph|lang|layout|line-break|list-image|nav|panose|punct|row|ruby|sep|size|src|tab-|table-border|text-(?:decor|trans)|top-bar|version|vnd|word-break)/.test(
                    name
                  ) ||
                  (/text\-indent|padding|margin/.test(name) && /\-[\d.]+/.test(value))
                ) {
                  continue
                }
                n[i] = name + ':' + parts[1]
              }
            }
            return tag + (n.length ? ' style="' + n.join(';').replace(/;{2,}/g, ';') + '"' : '')
          })
      }

      return function(html) {
        return isWordDocument(html) ? filterPasteWord(html) : html
      }
    })())
  ;(function() {
    var uNode = (UE.uNode = function(obj) {
      this.type = obj.type
      this.data = obj.data
      this.tagName = obj.tagName
      this.parentNode = obj.parentNode
      this.attrs = obj.attrs || {}
      this.children = obj.children
    })
    var notTransAttrs = { href: 1, src: 1, _src: 1, _href: 1, cdata_data: 1 }
    var notTransTagName = { style: 1, script: 1 }
    var indentChar = '    '
    var breakChar = '\n'

    function insertLine(arr, current, begin) {
      arr.push(breakChar)
      return current + (begin ? 1 : -1)
    }

    function insertIndent(arr, current) {
      for (var i = 0; i < current; i++) {
        arr.push(indentChar)
      }
    }

    uNode.createElement = function(html) {
      if (/[<>]/.test(html)) {
        return UE.htmlparser(html).children[0]
      } else {
        return new uNode({ type: 'element', children: [], tagName: html })
      }
    }
    uNode.createText = function(data, noTrans) {
      return new UE.uNode({ type: 'text', data: noTrans ? data : utils.unhtml(data || '') })
    }

    function nodeToHtml(node, arr, formatter, current) {
      switch (node.type) {
        case 'root':
          for (var i = 0, ci; (ci = node.children[i++]);) {
            if (formatter && ci.type == 'element' && !dtd.$inlineWithA[ci.tagName] && i > 1) {
              insertLine(arr, current, true)
              insertIndent(arr, current)
            }
            nodeToHtml(ci, arr, formatter, current)
          }
          break
        case 'text':
          isText(node, arr)
          break
        case 'element':
          isElement(node, arr, formatter, current)
          break
        case 'comment':
          isComment(node, arr, formatter)
      }
      return arr
    }

    function isText(node, arr) {
      if (node.parentNode.tagName == 'pre') {
        arr.push(node.data)
      } else {
        arr.push(
          notTransTagName[node.parentNode.tagName] ? utils.html(node.data) : node.data.replace(/[ ]{2}/g, ' &nbsp;')
        )
      }
    }

    function isElement(node, arr, formatter, current) {
      var attrhtml = ''
      if (node.attrs) {
        attrhtml = []
        var attrs = node.attrs
        for (var a in attrs) {
          attrhtml.push(
            a +
            (attrs[a] !== undefined
              ? '="' +
              (notTransAttrs[a]
                ? utils.html(attrs[a]).replace(/["]/g, function(a) {
                  return '&quot;'
                })
                : utils.unhtml(attrs[a])) +
              '"'
              : '')
          )
        }
        attrhtml = attrhtml.join(' ')
      }
      arr.push('<' + node.tagName + (attrhtml ? ' ' + attrhtml : '') + (dtd.$empty[node.tagName] ? '/' : '') + '>')
      if (formatter && !dtd.$inlineWithA[node.tagName] && node.tagName != 'pre') {
        if (node.children && node.children.length) {
          current = insertLine(arr, current, true)
          insertIndent(arr, current)
        }
      }
      if (node.children && node.children.length) {
        for (var i = 0, ci; (ci = node.children[i++]);) {
          if (formatter && ci.type == 'element' && !dtd.$inlineWithA[ci.tagName] && i > 1) {
            insertLine(arr, current)
            insertIndent(arr, current)
          }
          nodeToHtml(ci, arr, formatter, current)
        }
      }
      if (!dtd.$empty[node.tagName]) {
        if (formatter && !dtd.$inlineWithA[node.tagName] && node.tagName != 'pre') {
          if (node.children && node.children.length) {
            current = insertLine(arr, current)
            insertIndent(arr, current)
          }
        }
        arr.push('</' + node.tagName + '>')
      }
    }

    function isComment(node, arr) {
      arr.push('<!--' + node.data + '-->')
    }

    function getNodeById(root, id) {
      var node
      if (root.type == 'element' && root.getAttr('id') == id) {
        return root
      }
      if (root.children && root.children.length) {
        for (var i = 0, ci; (ci = root.children[i++]);) {
          if ((node = getNodeById(ci, id))) {
            return node
          }
        }
      }
    }

    function getNodesByTagName(node, tagName, arr) {
      if (node.type == 'element' && node.tagName == tagName) {
        arr.push(node)
      }
      if (node.children && node.children.length) {
        for (var i = 0, ci; (ci = node.children[i++]);) {
          getNodesByTagName(ci, tagName, arr)
        }
      }
    }

    function nodeTraversal(root, fn) {
      if (root.children && root.children.length) {
        for (var i = 0, ci; (ci = root.children[i]);) {
          nodeTraversal(ci, fn)
          if (ci.parentNode) {
            if (ci.children && ci.children.length) {
              fn(ci)
            }
            if (ci.parentNode) {
              i++
            }
          }
        }
      } else {
        fn(root)
      }
    }

    uNode.prototype = {
      toHtml: function(formatter) {
        var arr = []
        nodeToHtml(this, arr, formatter, 0)
        return arr.join('')
      },
      innerHTML: function(htmlstr) {
        if (this.type != 'element' || dtd.$empty[this.tagName]) {
          return this
        }
        if (utils.isString(htmlstr)) {
          if (this.children) {
            for (var i = 0, ci; (ci = this.children[i++]);) {
              ci.parentNode = null
            }
          }
          this.children = []
          var tmpRoot = UE.htmlparser(htmlstr)
          for (var i = 0, ci; (ci = tmpRoot.children[i++]);) {
            this.children.push(ci)
            ci.parentNode = this
          }
          return this
        } else {
          var tmpRoot = new UE.uNode({ type: 'root', children: this.children })
          return tmpRoot.toHtml()
        }
      },
      innerText: function(textStr, noTrans) {
        if (this.type != 'element' || dtd.$empty[this.tagName]) {
          return this
        }
        if (textStr) {
          if (this.children) {
            for (var i = 0, ci; (ci = this.children[i++]);) {
              ci.parentNode = null
            }
          }
          this.children = []
          this.appendChild(uNode.createText(textStr, noTrans))
          return this
        } else {
          return this.toHtml().replace(/<[^>]+>/g, '')
        }
      },
      getData: function() {
        if (this.type == 'element') {
          return ''
        }
        return this.data
      },
      firstChild: function() {
        return this.children ? this.children[0] : null
      },
      lastChild: function() {
        return this.children ? this.children[this.children.length - 1] : null
      },
      previousSibling: function() {
        var parent = this.parentNode
        for (var i = 0, ci; (ci = parent.children[i]); i++) {
          if (ci === this) {
            return i == 0 ? null : parent.children[i - 1]
          }
        }
      },
      nextSibling: function() {
        var parent = this.parentNode
        for (var i = 0, ci; (ci = parent.children[i++]);) {
          if (ci === this) {
            return parent.children[i]
          }
        }
      },
      replaceChild: function(target, source) {
        if (this.children) {
          if (target.parentNode) {
            target.parentNode.removeChild(target)
          }
          for (var i = 0, ci; (ci = this.children[i]); i++) {
            if (ci === source) {
              this.children.splice(i, 1, target)
              source.parentNode = null
              target.parentNode = this
              return target
            }
          }
        }
      },
      appendChild: function(node) {
        if (this.type == 'root' || (this.type == 'element' && !dtd.$empty[this.tagName])) {
          if (!this.children) {
            this.children = []
          }
          if (node.parentNode) {
            node.parentNode.removeChild(node)
          }
          for (var i = 0, ci; (ci = this.children[i]); i++) {
            if (ci === node) {
              this.children.splice(i, 1)
              break
            }
          }
          this.children.push(node)
          node.parentNode = this
          return node
        }
      },
      insertBefore: function(target, source) {
        if (this.children) {
          if (target.parentNode) {
            target.parentNode.removeChild(target)
          }
          for (var i = 0, ci; (ci = this.children[i]); i++) {
            if (ci === source) {
              this.children.splice(i, 0, target)
              target.parentNode = this
              return target
            }
          }
        }
      },
      insertAfter: function(target, source) {
        if (this.children) {
          if (target.parentNode) {
            target.parentNode.removeChild(target)
          }
          for (var i = 0, ci; (ci = this.children[i]); i++) {
            if (ci === source) {
              this.children.splice(i + 1, 0, target)
              target.parentNode = this
              return target
            }
          }
        }
      },
      removeChild: function(node, keepChildren) {
        if (this.children) {
          for (var i = 0, ci; (ci = this.children[i]); i++) {
            if (ci === node) {
              this.children.splice(i, 1)
              ci.parentNode = null
              if (keepChildren && ci.children && ci.children.length) {
                for (var j = 0, cj; (cj = ci.children[j]); j++) {
                  this.children.splice(i + j, 0, cj)
                  cj.parentNode = this
                }
              }
              return ci
            }
          }
        }
      },
      getAttr: function(attrName) {
        return this.attrs && this.attrs[attrName.toLowerCase()]
      },
      setAttr: function(attrName, attrVal) {
        if (!attrName) {
          delete this.attrs
          return
        }
        if (!this.attrs) {
          this.attrs = {}
        }
        if (utils.isObject(attrName)) {
          for (var a in attrName) {
            if (!attrName[a]) {
              delete this.attrs[a]
            } else {
              this.attrs[a.toLowerCase()] = attrName[a]
            }
          }
        } else {
          if (!attrVal) {
            delete this.attrs[attrName]
          } else {
            this.attrs[attrName.toLowerCase()] = attrVal
          }
        }
      },
      getIndex: function() {
        var parent = this.parentNode
        for (var i = 0, ci; (ci = parent.children[i]); i++) {
          if (ci === this) {
            return i
          }
        }
        return -1
      },
      getNodeById: function(id) {
        var node
        if (this.children && this.children.length) {
          for (var i = 0, ci; (ci = this.children[i++]);) {
            if ((node = getNodeById(ci, id))) {
              return node
            }
          }
        }
      },
      getNodesByTagName: function(tagNames) {
        tagNames = utils
          .trim(tagNames)
          .replace(/[ ]{2,}/g, ' ')
          .split(' ')
        var arr = []
        var me = this
        utils.each(tagNames, function(tagName) {
          if (me.children && me.children.length) {
            for (var i = 0, ci; (ci = me.children[i++]);) {
              getNodesByTagName(ci, tagName, arr)
            }
          }
        })
        return arr
      },
      getStyle: function(name) {
        var cssStyle = this.getAttr('style')
        if (!cssStyle) {
          return ''
        }
        var reg = new RegExp('(^|;)\\s*' + name + ':([^;]+)', 'i')
        var match = cssStyle.match(reg)
        if (match && match[0]) {
          return match[2]
        }
        return ''
      },
      setStyle: function(name, val) {
        function exec(name, val) {
          var reg = new RegExp('(^|;)\\s*' + name + ':([^;]+;?)', 'gi')
          cssStyle = cssStyle.replace(reg, '$1')
          if (val) {
            cssStyle = name + ':' + utils.unhtml(val) + ';' + cssStyle
          }
        }

        var cssStyle = this.getAttr('style')
        if (!cssStyle) {
          cssStyle = ''
        }
        if (utils.isObject(name)) {
          for (var a in name) {
            exec(a, name[a])
          }
        } else {
          exec(name, val)
        }
        this.setAttr('style', utils.trim(cssStyle))
      },
      traversal: function(fn) {
        if (this.children && this.children.length) {
          nodeTraversal(this, fn)
        }
        return this
      }
    }
  })()
  var htmlparser = (UE.htmlparser = function(htmlstr, ignoreBlank) {
    var re_tag = /<(?:(?:\/([^>]+)>)|(?:!--([\S|\s]*?)-->)|(?:([^\s\/<>]+)\s*((?:(?:"[^"]*")|(?:'[^']*')|[^"'<>])*)\/?>))/g
    var re_attr = /([\w\-:.]+)(?:(?:\s*=\s*(?:(?:"([^"]*)")|(?:'([^']*)')|([^\s>]+)))|(?=\s|$))/g
    var allowEmptyTags = {
      b: 1,
      code: 1,
      i: 1,
      u: 1,
      strike: 1,
      s: 1,
      tt: 1,
      strong: 1,
      q: 1,
      samp: 1,
      em: 1,
      span: 1,
      sub: 1,
      img: 1,
      sup: 1,
      font: 1,
      big: 1,
      small: 1,
      iframe: 1,
      a: 1,
      br: 1,
      pre: 1
    }
    htmlstr = htmlstr.replace(new RegExp(domUtils.fillChar, 'g'), '')
    if (!ignoreBlank) {
      htmlstr = htmlstr.replace(
        new RegExp(
          '[\\r\\t\\n' +
          (ignoreBlank ? '' : ' ') +
          ']*</?(\\w+)\\s*(?:[^>]*)>[\\r\\t\\n' +
          (ignoreBlank ? '' : ' ') +
          ']*',
          'g'
        ),
        function(a, b) {
          if (b && allowEmptyTags[b.toLowerCase()]) {
            return a.replace(/(^[\n\r]+)|([\n\r]+$)/g, '')
          }
          return a
            .replace(new RegExp('^[\\r\\n' + (ignoreBlank ? '' : ' ') + ']+'), '')
            .replace(new RegExp('[\\r\\n' + (ignoreBlank ? '' : ' ') + ']+$'), '')
        }
      )
    }
    var notTransAttrs = { href: 1, src: 1 }
    var uNode = UE.uNode
    var needParentNode = {
      td: 'tr',
      tr: ['tbody', 'thead', 'tfoot'],
      tbody: 'table',
      th: 'tr',
      thead: 'table',
      tfoot: 'table',
      caption: 'table',
      li: ['ul', 'ol'],
      dt: 'dl',
      dd: 'dl',
      option: 'select'
    }
    var needChild = { ol: 'li', ul: 'li' }

    function text(parent, data) {
      if (needChild[parent.tagName]) {
        var tmpNode = uNode.createElement(needChild[parent.tagName])
        parent.appendChild(tmpNode)
        tmpNode.appendChild(uNode.createText(data))
        parent = tmpNode
      } else {
        parent.appendChild(uNode.createText(data))
      }
    }

    function element(parent, tagName, htmlattr) {
      var needParentTag
      if ((needParentTag = needParentNode[tagName])) {
        var tmpParent = parent
        var hasParent
        while (tmpParent.type != 'root') {
          if (
            utils.isArray(needParentTag)
              ? utils.indexOf(needParentTag, tmpParent.tagName) != -1
              : needParentTag == tmpParent.tagName
          ) {
            parent = tmpParent
            hasParent = true
            break
          }
          tmpParent = tmpParent.parentNode
        }
        if (!hasParent) {
          parent = element(parent, utils.isArray(needParentTag) ? needParentTag[0] : needParentTag)
        }
      }
      var elm = new uNode({
        parentNode: parent,
        type: 'element',
        tagName: tagName.toLowerCase(),
        children: dtd.$empty[tagName] ? null : []
      })
      if (htmlattr) {
        var attrs = {}
        var match
        while ((match = re_attr.exec(htmlattr))) {
          attrs[match[1].toLowerCase()] = notTransAttrs[match[1].toLowerCase()]
            ? match[2] || match[3] || match[4]
            : utils.unhtml(match[2] || match[3] || match[4])
        }
        elm.attrs = attrs
      }
      parent.children.push(elm)
      return dtd.$empty[tagName] ? parent : elm
    }

    function comment(parent, data) {
      parent.children.push(new uNode({ type: 'comment', data: data, parentNode: parent }))
    }

    var match
    var currentIndex = 0
    var nextIndex = 0
    var root = new uNode({ type: 'root', children: [] })
    var currentParent = root
    while ((match = re_tag.exec(htmlstr))) {
      currentIndex = match.index
      try {
        if (currentIndex > nextIndex) {
          text(currentParent, htmlstr.slice(nextIndex, currentIndex))
        }
        if (match[3]) {
          if (dtd.$cdata[currentParent.tagName]) {
            text(currentParent, match[0])
          } else {
            currentParent = element(currentParent, match[3].toLowerCase(), match[4])
          }
        } else {
          if (match[1]) {
            if (currentParent.type != 'root') {
              if (dtd.$cdata[currentParent.tagName] && !dtd.$cdata[match[1]]) {
                text(currentParent, match[0])
              } else {
                var tmpParent = currentParent
                while (currentParent.type == 'element' && currentParent.tagName != match[1].toLowerCase()) {
                  currentParent = currentParent.parentNode
                  if (currentParent.type == 'root') {
                    currentParent = tmpParent
                    throw 'break'
                  }
                }
                currentParent = currentParent.parentNode
              }
            }
          } else {
            if (match[2]) {
              comment(currentParent, match[2])
            }
          }
        }
      } catch (e) {
      }
      nextIndex = re_tag.lastIndex
    }
    if (nextIndex < htmlstr.length) {
      text(currentParent, htmlstr.slice(nextIndex))
    }
    return root
  })
  var filterNode = (UE.filterNode = (function() {
    function filterNode(node, rules) {
      switch (node.type) {
        case 'text':
          break
        case 'element':
          var val
          if ((val = rules[node.tagName])) {
            if (val === '-') {
              node.parentNode.removeChild(node)
            } else {
              if (utils.isFunction(val)) {
                var parentNode = node.parentNode
                var index = node.getIndex()
                val(node)
                if (node.parentNode) {
                  if (node.children) {
                    for (var i = 0, ci; (ci = node.children[i]);) {
                      filterNode(ci, rules)
                      if (ci.parentNode) {
                        i++
                      }
                    }
                  }
                } else {
                  for (var i = index, ci; (ci = parentNode.children[i]);) {
                    filterNode(ci, rules)
                    if (ci.parentNode) {
                      i++
                    }
                  }
                }
              } else {
                var attrs = val['$']
                if (attrs && node.attrs) {
                  var tmpAttrs = {}
                  var tmpVal
                  for (var a in attrs) {
                    tmpVal = node.getAttr(a)
                    if (a == 'style' && utils.isArray(attrs[a])) {
                      var tmpCssStyle = []
                      utils.each(attrs[a], function(v) {
                        var tmp
                        if ((tmp = node.getStyle(v))) {
                          tmpCssStyle.push(v + ':' + tmp)
                        }
                      })
                      tmpVal = tmpCssStyle.join(';')
                    }
                    if (tmpVal) {
                      tmpAttrs[a] = tmpVal
                    }
                  }
                  node.attrs = tmpAttrs
                }
                if (node.children) {
                  for (var i = 0, ci; (ci = node.children[i]);) {
                    filterNode(ci, rules)
                    if (ci.parentNode) {
                      i++
                    }
                  }
                }
              }
            }
          } else {
            if (dtd.$cdata[node.tagName]) {
              node.parentNode.removeChild(node)
            } else {
              var parentNode = node.parentNode
              var index = node.getIndex()
              node.parentNode.removeChild(node, true)
              for (var i = index, ci; (ci = parentNode.children[i]);) {
                filterNode(ci, rules)
                if (ci.parentNode) {
                  i++
                }
              }
            }
          }
          break
        case 'comment':
          node.parentNode.removeChild(node)
      }
    }

    return function(root, rules) {
      if (utils.isEmptyObject(rules)) {
        return root
      }
      var val
      if ((val = rules['-'])) {
        utils.each(val.split(' '), function(k) {
          rules[k] = '-'
        })
      }
      for (var i = 0, ci; (ci = root.children[i]);) {
        filterNode(ci, rules)
        if (ci.parentNode) {
          i++
        }
      }
      return root
    }
  })())
  UE.plugin = (function() {
    var _plugins = {}
    return {
      register: function(pluginName, fn, oldOptionName, afterDisabled) {
        if (oldOptionName && utils.isFunction(oldOptionName)) {
          afterDisabled = oldOptionName
          oldOptionName = null
        }
        _plugins[pluginName] = { optionName: oldOptionName || pluginName, execFn: fn, afterDisabled: afterDisabled }
      },
      load: function(editor) {
        utils.each(_plugins, function(plugin) {
          var _export = plugin.execFn.call(editor)
          if (editor.options[plugin.optionName] !== false) {
            if (_export) {
              utils.each(_export, function(v, k) {
                switch (k.toLowerCase()) {
                  case 'shortcutkey':
                    editor.addshortcutkey(v)
                    break
                  case 'bindevents':
                    utils.each(v, function(fn, eventName) {
                      editor.addListener(eventName, fn)
                    })
                    break
                  case 'bindmultievents':
                    utils.each(utils.isArray(v) ? v : [v], function(event) {
                      var types = utils.trim(event.type).split(/\s+/)
                      utils.each(types, function(eventName) {
                        editor.addListener(eventName, event.handler)
                      })
                    })
                    break
                  case 'commands':
                    utils.each(v, function(execFn, execName) {
                      editor.commands[execName] = execFn
                    })
                    break
                  case 'outputrule':
                    editor.addOutputRule(v)
                    break
                  case 'inputrule':
                    editor.addInputRule(v)
                    break
                  case 'defaultoptions':
                    editor.setOpt(v)
                }
              })
            }
          } else {
            if (plugin.afterDisabled) {
              plugin.afterDisabled.call(editor)
            }
          }
        })
        utils.each(UE.plugins, function(plugin) {
          plugin.call(editor)
        })
      },
      run: function(pluginName, editor) {
        var plugin = _plugins[pluginName]
        if (plugin) {
          plugin.exeFn.call(editor)
        }
      }
    }
  })()
  var keymap = (UE.keymap = {
    Backspace: 8,
    Tab: 9,
    Enter: 13,
    Shift: 16,
    Control: 17,
    Alt: 18,
    CapsLock: 20,
    Esc: 27,
    Spacebar: 32,
    PageUp: 33,
    PageDown: 34,
    End: 35,
    Home: 36,
    Left: 37,
    Up: 38,
    Right: 39,
    Down: 40,
    Insert: 45,
    Del: 46,
    NumLock: 144,
    Cmd: 91,
    '=': 187,
    '-': 189,
    b: 66,
    i: 73,
    z: 90,
    y: 89,
    v: 86,
    x: 88,
    s: 83,
    n: 78
  })
  var LocalStorage = (UE.LocalStorage = (function() {
      var storage = window.localStorage || getUserData() || null
      var LOCAL_FILE = 'localStorage'
      return {
        saveLocalData: function(key, data) {
          if (storage && data) {
            storage.setItem(key, data)
            return true
          }
          return false
        },
        getLocalData: function(key) {
          if (storage) {
            return storage.getItem(key)
          }
          return null
        },
        removeItem: function(key) {
          storage && storage.removeItem(key)
        }
      }

      function getUserData() {
        var container = document.createElement('div')
        container.style.display = 'none'
        if (!container.addBehavior) {
          return null
        }
        container.addBehavior('#default#userdata')
        return {
          getItem: function(key) {
            var result = null
            try {
              document.body.appendChild(container)
              container.load(LOCAL_FILE)
              result = container.getAttribute(key)
              document.body.removeChild(container)
            } catch (e) {
            }
            return result
          },
          setItem: function(key, value) {
            document.body.appendChild(container)
            container.setAttribute(key, value)
            container.save(LOCAL_FILE)
            document.body.removeChild(container)
          },
          removeItem: function(key) {
            document.body.appendChild(container)
            container.removeAttribute(key)
            container.save(LOCAL_FILE)
            document.body.removeChild(container)
          }
        }
      }
    })())
  ;(function() {
    var ROOTKEY = 'ueditor_preference'
    UE.Editor.prototype.setPreferences = function(key, value) {
      var obj = {}
      if (utils.isString(key)) {
        obj[key] = value
      } else {
        obj = key
      }
      var data = LocalStorage.getLocalData(ROOTKEY)
      if (data && (data = utils.str2json(data))) {
        utils.extend(data, obj)
      } else {
        data = obj
      }
      data && LocalStorage.saveLocalData(ROOTKEY, utils.json2str(data))
    }
    UE.Editor.prototype.getPreferences = function(key) {
      var data = LocalStorage.getLocalData(ROOTKEY)
      if (data && (data = utils.str2json(data))) {
        return key ? data[key] : data
      }
      return null
    }
    UE.Editor.prototype.removePreferences = function(key) {
      var data = LocalStorage.getLocalData(ROOTKEY)
      if (data && (data = utils.str2json(data))) {
        data[key] = undefined
        delete data[key]
      }
      data && LocalStorage.saveLocalData(ROOTKEY, utils.json2str(data))
    }
  })()
  UE.plugins['defaultfilter'] = function() {
    var me = this
    me.setOpt({ allowDivTransToP: true, disabledTableInTable: true })
    me.addInputRule(function(root) {
      var allowDivTransToP = this.options.allowDivTransToP
      var val

      function tdParent(node) {
        while (node && node.type == 'element') {
          if (node.tagName == 'td') {
            return true
          }
          node = node.parentNode
        }
        return false
      }

      root.traversal(function(node) {
        if (node.type == 'element') {
          if (
            !dtd.$cdata[node.tagName] &&
            me.options.autoClearEmptyNode &&
            dtd.$inline[node.tagName] &&
            !dtd.$empty[node.tagName] &&
            (!node.attrs || utils.isEmptyObject(node.attrs))
          ) {
            if (!node.firstChild()) {
              node.parentNode.removeChild(node)
            } else {
              if (node.tagName == 'span' && (!node.attrs || utils.isEmptyObject(node.attrs))) {
                node.parentNode.removeChild(node, true)
              }
            }
            return
          }
          switch (node.tagName) {
            case 'style':
            case 'script':
              node.setAttr({ cdata_tag: node.tagName, cdata_data: node.innerHTML() || '', _ue_custom_node_: 'true' })
              node.tagName = 'div'
              node.innerHTML('')
              break
            case 'a':
              if ((val = node.getAttr('href'))) {
                node.setAttr('_href', val)
              }
              break
            case 'img':
              if ((val = node.getAttr('src'))) {
                if (/^data:/.test(val)) {
                  node.parentNode.removeChild(node)
                  break
                }
              }
              node.setAttr('_src', node.getAttr('src'))
              break
            case 'span':
              if (browser.webkit && (val = node.getStyle('white-space'))) {
                if (/nowrap|normal/.test(val)) {
                  node.setStyle('white-space', '')
                  if (me.options.autoClearEmptyNode && utils.isEmptyObject(node.attrs)) {
                    node.parentNode.removeChild(node, true)
                  }
                }
              }
              val = node.getAttr('id')
              if (val && /^_baidu_bookmark_/i.test(val)) {
                node.parentNode.removeChild(node)
              }
              break
            case 'p':
              if ((val = node.getAttr('align'))) {
                node.setAttr('align')
                node.setStyle('text-align', val)
              }
              utils.each(node.children, function(n) {
                if (n.type == 'element' && n.tagName == 'p') {
                  var next = n.nextSibling()
                  node.parentNode.insertAfter(n, node)
                  var last = n
                  while (next) {
                    var tmp = next.nextSibling()
                    node.parentNode.insertAfter(next, last)
                    last = next
                    next = tmp
                  }
                  return false
                }
              })
              if (!node.firstChild()) {
                node.innerHTML(browser.ie ? '&nbsp;' : '<br/>')
              }
              break
            case 'div':
              if (node.getAttr('cdata_tag')) {
                break
              }
              val = node.getAttr('class')
              if (val && /^line number\d+/.test(val)) {
                break
              }
              if (!allowDivTransToP) {
                break
              }
              var tmpNode
              var p = UE.uNode.createElement('p')
              while ((tmpNode = node.firstChild())) {
                if (tmpNode.type == 'text' || !UE.dom.dtd.$block[tmpNode.tagName]) {
                  p.appendChild(tmpNode)
                } else {
                  if (p.firstChild()) {
                    node.parentNode.insertBefore(p, node)
                    p = UE.uNode.createElement('p')
                  } else {
                    node.parentNode.insertBefore(tmpNode, node)
                  }
                }
              }
              if (p.firstChild()) {
                node.parentNode.insertBefore(p, node)
              }
              node.parentNode.removeChild(node)
              break
            case 'dl':
              node.tagName = 'ul'
              break
            case 'dt':
            case 'dd':
              node.tagName = 'li'
              break
            case 'li':
              var className = node.getAttr('class')
              if (!className || !/list\-/.test(className)) {
                node.setAttr()
              }
              var tmpNodes = node.getNodesByTagName('ol ul')
              UE.utils.each(tmpNodes, function(n) {
                node.parentNode.insertAfter(n, node)
              })
              break
            case 'td':
            case 'th':
            case 'caption':
              if (!node.children || !node.children.length) {
                node.appendChild(browser.ie11below ? UE.uNode.createText(' ') : UE.uNode.createElement('br'))
              }
              break
            case 'table':
              if (me.options.disabledTableInTable && tdParent(node)) {
                node.parentNode.insertBefore(UE.uNode.createText(node.innerText()), node)
                node.parentNode.removeChild(node)
              }
          }
        }
      })
    })
    me.addOutputRule(function(root) {
      var val
      root.traversal(function(node) {
        if (node.type == 'element') {
          if (
            me.options.autoClearEmptyNode &&
            dtd.$inline[node.tagName] &&
            !dtd.$empty[node.tagName] &&
            (!node.attrs || utils.isEmptyObject(node.attrs))
          ) {
            if (!node.firstChild()) {
              node.parentNode.removeChild(node)
            } else {
              if (node.tagName == 'span' && (!node.attrs || utils.isEmptyObject(node.attrs))) {
                node.parentNode.removeChild(node, true)
              }
            }
            return
          }
          switch (node.tagName) {
            case 'div':
              if ((val = node.getAttr('cdata_tag'))) {
                node.tagName = val
                node.appendChild(UE.uNode.createText(node.getAttr('cdata_data')))
                node.setAttr({ cdata_tag: '', cdata_data: '', _ue_custom_node_: '' })
              }
              break
            case 'a':
              if ((val = node.getAttr('_href'))) {
                node.setAttr({ href: utils.html(val), _href: '' })
              }
              break
              break
            case 'span':
              val = node.getAttr('id')
              if (val && /^_baidu_bookmark_/i.test(val)) {
                node.parentNode.removeChild(node)
              }
              break
            case 'img':
              if ((val = node.getAttr('_src'))) {
                node.setAttr({ src: node.getAttr('_src'), _src: '' })
              }
          }
        }
      })
    })
  }
  UE.commands['inserthtml'] = {
    execCommand: function(command, html, notNeedFilter) {
      var me = this
      var range
      var div
      if (!html) {
        return
      }
      if (me.fireEvent('beforeinserthtml', html) === true) {
        return
      }
      range = me.selection.getRange()
      div = range.document.createElement('div')
      div.style.display = 'inline'
      if (!notNeedFilter) {
        var root = UE.htmlparser(html)
        if (me.options.filterRules) {
          UE.filterNode(root, me.options.filterRules)
        }
        me.filterInputRule(root)
        html = root.toHtml()
      }
      div.innerHTML = utils.trim(html)
      if (!range.collapsed) {
        var tmpNode = range.startContainer
        if (domUtils.isFillChar(tmpNode)) {
          range.setStartBefore(tmpNode)
        }
        tmpNode = range.endContainer
        if (domUtils.isFillChar(tmpNode)) {
          range.setEndAfter(tmpNode)
        }
        range.txtToElmBoundary()
        if (range.endContainer && range.endContainer.nodeType == 1) {
          tmpNode = range.endContainer.childNodes[range.endOffset]
          if (tmpNode && domUtils.isBr(tmpNode)) {
            range.setEndAfter(tmpNode)
          }
        }
        if (range.startOffset == 0) {
          tmpNode = range.startContainer
          if (domUtils.isBoundaryNode(tmpNode, 'firstChild')) {
            tmpNode = range.endContainer
            if (
              range.endOffset == (tmpNode.nodeType == 3 ? tmpNode.nodeValue.length : tmpNode.childNodes.length) &&
              domUtils.isBoundaryNode(tmpNode, 'lastChild')
            ) {
              me.body.innerHTML = '<p style="font-size:18px">' + (browser.ie ? '' : '<br/>') + '</p>'
              range.setStart(me.body.firstChild, 0).collapse(true)
            }
          }
        }
        !range.collapsed && range.deleteContents()
        if (range.startContainer.nodeType == 1) {
          var child = range.startContainer.childNodes[range.startOffset]
          var pre
          if (child && domUtils.isBlockElm(child) && (pre = child.previousSibling) && domUtils.isBlockElm(pre)) {
            range.setEnd(pre, pre.childNodes.length).collapse()
            while (child.firstChild) {
              pre.appendChild(child.firstChild)
            }
            domUtils.remove(child)
          }
        }
      }
      var child
      var parent
      var pre
      var tmp
      var hadBreak = 0
      var nextNode
      if (range.inFillChar()) {
        child = range.startContainer
        if (domUtils.isFillChar(child)) {
          range.setStartBefore(child).collapse(true)
          domUtils.remove(child)
        } else {
          if (domUtils.isFillChar(child, true)) {
            child.nodeValue = child.nodeValue.replace(fillCharReg, '')
            range.startOffset--
            range.collapsed && range.collapse(true)
          }
        }
      }
      var li = domUtils.findParentByTagName(range.startContainer, 'li', true)
      if (li) {
        var next, last
        while ((child = div.firstChild)) {
          while (child && (child.nodeType == 3 || !domUtils.isBlockElm(child) || child.tagName == 'HR')) {
            next = child.nextSibling
            range.insertNode(child).collapse()
            last = child
            child = next
          }
          if (child) {
            if (/^(ol|ul)$/i.test(child.tagName)) {
              while (child.firstChild) {
                last = child.firstChild
                domUtils.insertAfter(li, child.firstChild)
                li = li.nextSibling
              }
              domUtils.remove(child)
            } else {
              var tmpLi
              next = child.nextSibling
              tmpLi = me.document.createElement('li')
              domUtils.insertAfter(li, tmpLi)
              tmpLi.appendChild(child)
              last = child
              child = next
              li = tmpLi
            }
          }
        }
        li = domUtils.findParentByTagName(range.startContainer, 'li', true)
        if (domUtils.isEmptyBlock(li)) {
          domUtils.remove(li)
        }
        if (last) {
          range
            .setStartAfter(last)
            .collapse(true)
            .select(true)
        }
      } else {
        while ((child = div.firstChild)) {
          if (hadBreak) {
            var p = me.document.createElement('p')
            while (child && (child.nodeType == 3 || !dtd.$block[child.tagName])) {
              nextNode = child.nextSibling
              p.appendChild(child)
              child = nextNode
            }
            if (p.firstChild) {
              child = p
            }
          }
          range.insertNode(child)
          nextNode = child.nextSibling
          if (!hadBreak && child.nodeType == domUtils.NODE_ELEMENT && domUtils.isBlockElm(child)) {
            parent = domUtils.findParent(child, function(node) {
              return domUtils.isBlockElm(node)
            })
            if (
              parent &&
              parent.tagName.toLowerCase() != 'body' &&
              !(dtd[parent.tagName][child.nodeName] && child.parentNode === parent)
            ) {
              if (!dtd[parent.tagName][child.nodeName]) {
                pre = parent
              } else {
                tmp = child.parentNode
                while (tmp !== parent) {
                  pre = tmp
                  tmp = tmp.parentNode
                }
              }
              domUtils.breakParent(child, pre || tmp)
              var pre = child.previousSibling
              domUtils.trimWhiteTextNode(pre)
              if (!pre.childNodes.length) {
                domUtils.remove(pre)
              }
              if (
                !browser.ie &&
                (next = child.nextSibling) &&
                domUtils.isBlockElm(next) &&
                next.lastChild &&
                !domUtils.isBr(next.lastChild)
              ) {
                next.appendChild(me.document.createElement('br'))
              }
              hadBreak = 1
            }
          }
          var next = child.nextSibling
          if (!div.firstChild && next && domUtils.isBlockElm(next)) {
            range.setStart(next, 0).collapse(true)
            break
          }
          range.setEndAfter(child).collapse()
        }
        child = range.startContainer
        if (nextNode && domUtils.isBr(nextNode)) {
          domUtils.remove(nextNode)
        }
        if (domUtils.isBlockElm(child) && domUtils.isEmptyNode(child)) {
          if ((nextNode = child.nextSibling)) {
            domUtils.remove(child)
            if (nextNode.nodeType == 1 && dtd.$block[nextNode.tagName]) {
              range
                .setStart(nextNode, 0)
                .collapse(true)
                .shrinkBoundary()
            }
          } else {
            try {
              child.innerHTML = browser.ie ? domUtils.fillChar : '<br/>'
            } catch (e) {
              range.setStartBefore(child)
              domUtils.remove(child)
            }
          }
        }
        try {
          range.select(true)
        } catch (e) {
        }
      }
      setTimeout(function() {
        range = me.selection.getRange()
        range.scrollToView(me.autoHeightEnabled, me.autoHeightEnabled ? domUtils.getXY(me.iframe).y : 0)
        me.fireEvent('afterinserthtml', html)
      }, 200)
    }
  }
  UE.plugins['autotypeset'] = function() {
    this.setOpt({
      autotypeset: {
        mergeEmptyline: true,
        removeClass: true,
        removeEmptyline: false,
        textAlign: 'left',
        imageBlockLine: 'center',
        pasteFilter: false,
        clearFontSize: false,
        clearFontFamily: false,
        removeEmptyNode: false,
        removeTagNames: utils.extend({ div: 1 }, dtd.$removeEmpty),
        indent: false,
        indentValue: '2em',
        bdc2sb: false,
        tobdc: false
      }
    })
    var me = this
    var opt = me.options.autotypeset
    var remainClass = { selectTdClass: 1, pagebreak: 1, anchorclass: 1 }
    var remainTag = { li: 1 }
    var tags = { div: 1, p: 1, blockquote: 1, center: 1, h1: 1, h2: 1, h3: 1, h4: 1, h5: 1, h6: 1, span: 1 }
    var highlightCont
    if (!opt) {
      return
    }
    readLocalOpts()

    function isLine(node, notEmpty) {
      if (!node || node.nodeType == 3) {
        return 0
      }
      if (domUtils.isBr(node)) {
        return 1
      }
      if (node && node.parentNode && tags[node.tagName.toLowerCase()]) {
        if ((highlightCont && highlightCont.contains(node)) || node.getAttribute('pagebreak')) {
          return 0
        }
        return notEmpty
          ? !domUtils.isEmptyBlock(node)
          : domUtils.isEmptyBlock(node, new RegExp('[\\s' + domUtils.fillChar + ']', 'g'))
      }
    }

    function removeNotAttributeSpan(node) {
      if (!node.style.cssText) {
        domUtils.removeAttributes(node, ['style'])
        if (node.tagName.toLowerCase() == 'span' && domUtils.hasNoAttributes(node)) {
          domUtils.remove(node, true)
        }
      }
    }

    function autotype(type, html) {
      var me = this
      var cont
      if (html) {
        if (!opt.pasteFilter) {
          return
        }
        cont = me.document.createElement('div')
        cont.innerHTML = html.html
      } else {
        cont = me.document.body
      }
      var nodes = domUtils.getElementsByTagName(cont, '*')
      for (var i = 0, ci; (ci = nodes[i++]);) {
        if (me.fireEvent('excludeNodeinautotype', ci) === true) {
          continue
        }
        if (opt.clearFontSize && ci.style.fontSize) {
          domUtils.removeStyle(ci, 'font-size')
          removeNotAttributeSpan(ci)
        }
        if (opt.clearFontFamily && ci.style.fontFamily) {
          domUtils.removeStyle(ci, 'font-family')
          removeNotAttributeSpan(ci)
        }
        if (isLine(ci)) {
          if (opt.mergeEmptyline) {
            var next = ci.nextSibling
            var tmpNode
            var isBr = domUtils.isBr(ci)
            while (isLine(next)) {
              tmpNode = next
              next = tmpNode.nextSibling
              if (isBr && (!next || (next && !domUtils.isBr(next)))) {
                break
              }
              domUtils.remove(tmpNode)
            }
          }
          if (opt.removeEmptyline && domUtils.inDoc(ci, cont) && !remainTag[ci.parentNode.tagName.toLowerCase()]) {
            if (domUtils.isBr(ci)) {
              next = ci.nextSibling
              if (next && !domUtils.isBr(next)) {
                continue
              }
            }
            domUtils.remove(ci)
            continue
          }
        }
        if (isLine(ci, true) && ci.tagName != 'SPAN') {
          if (opt.indent) {
            ci.style.textIndent = opt.indentValue
          }
          if (opt.textAlign) {
            ci.style.textAlign = opt.textAlign
          }
        }
        if (opt.removeClass && ci.className && !remainClass[ci.className.toLowerCase()]) {
          if (highlightCont && highlightCont.contains(ci)) {
            continue
          }
          domUtils.removeAttributes(ci, ['class'])
        }
        if (opt.imageBlockLine && ci.tagName.toLowerCase() == 'img' && !ci.getAttribute('emotion')) {
          if (html) {
            var img = ci
            switch (opt.imageBlockLine) {
              case 'left':
              case 'right':
              case 'none':
                var pN = img.parentNode
                var tmpNode
                var pre
                var next
                while (dtd.$inline[pN.tagName] || pN.tagName == 'A') {
                  pN = pN.parentNode
                }
                tmpNode = pN
                if (tmpNode.tagName == 'P' && domUtils.getStyle(tmpNode, 'text-align') == 'center') {
                  if (
                    !domUtils.isBody(tmpNode) &&
                    domUtils.getChildCount(tmpNode, function(node) {
                      return !domUtils.isBr(node) && !domUtils.isWhitespace(node)
                    }) == 1
                  ) {
                    pre = tmpNode.previousSibling
                    next = tmpNode.nextSibling
                    if (
                      pre &&
                      next &&
                      pre.nodeType == 1 &&
                      next.nodeType == 1 &&
                      pre.tagName == next.tagName &&
                      domUtils.isBlockElm(pre)
                    ) {
                      pre.appendChild(tmpNode.firstChild)
                      while (next.firstChild) {
                        pre.appendChild(next.firstChild)
                      }
                      domUtils.remove(tmpNode)
                      domUtils.remove(next)
                    } else {
                      domUtils.setStyle(tmpNode, 'text-align', '')
                    }
                  }
                }
                domUtils.setStyle(img, 'float', opt.imageBlockLine)
                break
              case 'center':
                if (me.queryCommandValue('imagefloat') != 'center') {
                  pN = img.parentNode
                  domUtils.setStyle(img, 'float', 'none')
                  tmpNode = img
                  while (
                    pN &&
                    domUtils.getChildCount(pN, function(node) {
                      return !domUtils.isBr(node) && !domUtils.isWhitespace(node)
                    }) == 1 &&
                    (dtd.$inline[pN.tagName] || pN.tagName == 'A')
                    ) {
                    tmpNode = pN
                    pN = pN.parentNode
                  }
                  var pNode = me.document.createElement('p')
                  domUtils.setAttributes(pNode, { style: 'text-align:center' })
                  tmpNode.parentNode.insertBefore(pNode, tmpNode)
                  pNode.appendChild(tmpNode)
                  domUtils.setStyle(tmpNode, 'float', '')
                }
            }
          } else {
            var range = me.selection.getRange()
            range.selectNode(ci).select()
            me.execCommand('imagefloat', opt.imageBlockLine)
          }
        }
        if (opt.removeEmptyNode) {
          if (
            opt.removeTagNames[ci.tagName.toLowerCase()] &&
            domUtils.hasNoAttributes(ci) &&
            domUtils.isEmptyBlock(ci)
          ) {
            domUtils.remove(ci)
          }
        }
      }
      if (opt.tobdc) {
        var root = UE.htmlparser(cont.innerHTML)
        root.traversal(function(node) {
          if (node.type == 'text') {
            node.data = ToDBC(node.data)
          }
        })
        cont.innerHTML = root.toHtml()
      }
      if (opt.bdc2sb) {
        var root = UE.htmlparser(cont.innerHTML)
        root.traversal(function(node) {
          if (node.type == 'text') {
            node.data = DBC2SB(node.data)
          }
        })
        cont.innerHTML = root.toHtml()
      }
      if (html) {
        html.html = cont.innerHTML
      }
    }

    if (opt.pasteFilter) {
      me.addListener('beforepaste', autotype)
    }

    function DBC2SB(str) {
      var result = ''
      for (var i = 0; i < str.length; i++) {
        var code = str.charCodeAt(i)
        if (code >= 65281 && code <= 65373) {
          result += String.fromCharCode(str.charCodeAt(i) - 65248)
        } else {
          if (code == 12288) {
            result += String.fromCharCode(str.charCodeAt(i) - 12288 + 32)
          } else {
            result += str.charAt(i)
          }
        }
      }
      return result
    }

    function ToDBC(txtstring) {
      txtstring = utils.html(txtstring)
      var tmp = ''
      var mark = ''
      for (var i = 0; i < txtstring.length; i++) {
        if (txtstring.charCodeAt(i) == 32) {
          tmp = tmp + String.fromCharCode(12288)
        } else {
          if (txtstring.charCodeAt(i) < 127) {
            tmp = tmp + String.fromCharCode(txtstring.charCodeAt(i) + 65248)
          } else {
            tmp += txtstring.charAt(i)
          }
        }
      }
      return tmp
    }

    function readLocalOpts() {
      var cookieOpt = me.getPreferences('autotypeset')
      utils.extend(me.options.autotypeset, cookieOpt)
    }

    me.commands['autotypeset'] = {
      execCommand: function() {
        me.removeListener('beforepaste', autotype)
        if (opt.pasteFilter) {
          me.addListener('beforepaste', autotype)
        }
        autotype.call(me)
      }
    }
  }
  UE.plugin.register('autosubmit', function() {
    return {
      shortcutkey: { autosubmit: 'ctrl+13' },
      commands: {
        autosubmit: {
          execCommand: function() {
            var me = this
            var form = domUtils.findParentByTagName(me.iframe, 'form', false)
            if (form) {
              if (me.fireEvent('beforesubmit') === false) {
                return
              }
              me.sync()
              form.submit()
            }
          }
        }
      }
    }
  })
  UE.plugin.register('background', function() {
    var me = this
    var cssRuleId = 'editor_background'
    var isSetColored
    var reg = new RegExp('body[\\s]*\\{(.+)\\}', 'i')

    function stringToObj(str) {
      var obj = {}
      var styles = str.split(';')
      utils.each(styles, function(v) {
        var index = v.indexOf(':')
        var key = utils.trim(v.substr(0, index)).toLowerCase()
        key && (obj[key] = utils.trim(v.substr(index + 1) || ''))
      })
      return obj
    }

    function setBackground(obj) {
      if (obj) {
        var styles = []
        for (var name in obj) {
          if (obj.hasOwnProperty(name)) {
            styles.push(name + ':' + obj[name] + '; ')
          }
        }
        utils.cssRule(cssRuleId, styles.length ? 'body{' + styles.join('') + '}' : '', me.document)
      } else {
        utils.cssRule(cssRuleId, '', me.document)
      }
    }

    var orgFn = me.hasContents
    me.hasContents = function() {
      if (me.queryCommandValue('background')) {
        return true
      }
      return orgFn.apply(me, arguments)
    }
    return {
      bindEvents: {
        getAllHtml: function(type, headHtml) {
          var body = this.body
          var su = domUtils.getComputedStyle(body, 'background-image')
          var url = ''
          if (su.indexOf(me.options.imagePath) > 0) {
            url = su.substring(su.indexOf(me.options.imagePath), su.length - 1).replace(/"|\(|\)/gi, '')
          } else {
            url = su != 'none' ? su.replace(/url\("?|"?\)/gi, '') : ''
          }
          var html = '<style type="text/css">body{'
          var bgObj = {
            'background-color': domUtils.getComputedStyle(body, 'background-color') || '#ffffff',
            'background-image': url ? 'url(' + url + ')' : '',
            'background-repeat': domUtils.getComputedStyle(body, 'background-repeat') || '',
            'background-position': browser.ie
              ? domUtils.getComputedStyle(body, 'background-position-x') +
              ' ' +
              domUtils.getComputedStyle(body, 'background-position-y')
              : domUtils.getComputedStyle(body, 'background-position'),
            height: domUtils.getComputedStyle(body, 'height')
          }
          for (var name in bgObj) {
            if (bgObj.hasOwnProperty(name)) {
              html += name + ':' + bgObj[name] + '; '
            }
          }
          html += '}</style> '
          headHtml.push(html)
        },
        aftersetcontent: function() {
          if (isSetColored == false) {
            setBackground()
          }
        }
      },
      inputRule: function(root) {
        isSetColored = false
        utils.each(root.getNodesByTagName('p'), function(p) {
          var styles = p.getAttr('data-background')
          if (styles) {
            isSetColored = true
            setBackground(stringToObj(styles))
            p.parentNode.removeChild(p)
          }
        })
      },
      outputRule: function(root) {
        var me = this
        var styles = (utils.cssRule(cssRuleId, me.document) || '').replace(/[\n\r]+/g, '').match(reg)
        if (styles) {
          root.appendChild(
            UE.uNode.createElement(
              '<p style="display:none;" data-background="' +
              utils.trim(styles[1].replace(/"/g, '').replace(/[\s]+/g, ' ')) +
              '"><br/></p>'
            )
          )
        }
      },
      commands: {
        background: {
          execCommand: function(cmd, obj) {
            setBackground(obj)
          },
          queryCommandValue: function() {
            var me = this
            var styles = (utils.cssRule(cssRuleId, me.document) || '').replace(/[\n\r]+/g, '').match(reg)
            return styles ? stringToObj(styles[1]) : null
          },
          notNeedUndo: true
        }
      }
    }
  })
  UE.commands['imagefloat'] = {
    execCommand: function(cmd, align) {
      var me = this
      var range = me.selection.getRange()
      if (!range.collapsed) {
        var img = range.getClosedNode()
        if (img && img.tagName == 'IMG') {
          switch (align) {
            case 'left':
            case 'right':
            case 'none':
              var pN = img.parentNode
              var tmpNode
              var pre
              var next
              while (dtd.$inline[pN.tagName] || pN.tagName == 'A') {
                pN = pN.parentNode
              }
              tmpNode = pN
              if (tmpNode.tagName == 'P' && domUtils.getStyle(tmpNode, 'text-align') == 'center') {
                if (
                  !domUtils.isBody(tmpNode) &&
                  domUtils.getChildCount(tmpNode, function(node) {
                    return !domUtils.isBr(node) && !domUtils.isWhitespace(node)
                  }) == 1
                ) {
                  pre = tmpNode.previousSibling
                  next = tmpNode.nextSibling
                  if (
                    pre &&
                    next &&
                    pre.nodeType == 1 &&
                    next.nodeType == 1 &&
                    pre.tagName == next.tagName &&
                    domUtils.isBlockElm(pre)
                  ) {
                    pre.appendChild(tmpNode.firstChild)
                    while (next.firstChild) {
                      pre.appendChild(next.firstChild)
                    }
                    domUtils.remove(tmpNode)
                    domUtils.remove(next)
                  } else {
                    domUtils.setStyle(tmpNode, 'text-align', '')
                  }
                }
                range.selectNode(img).select()
              }
              domUtils.setStyle(img, 'float', align == 'none' ? '' : align)
              if (align == 'none') {
                domUtils.removeAttributes(img, 'align')
              }
              break
            case 'center':
              if (me.queryCommandValue('imagefloat') != 'center') {
                pN = img.parentNode
                domUtils.setStyle(img, 'float', '')
                domUtils.removeAttributes(img, 'align')
                tmpNode = img
                while (
                  pN &&
                  domUtils.getChildCount(pN, function(node) {
                    return !domUtils.isBr(node) && !domUtils.isWhitespace(node)
                  }) == 1 &&
                  (dtd.$inline[pN.tagName] || pN.tagName == 'A')
                  ) {
                  tmpNode = pN
                  pN = pN.parentNode
                }
                range.setStartBefore(tmpNode).setCursor(false)
                pN = me.document.createElement('div')
                pN.appendChild(tmpNode)
                domUtils.setStyle(tmpNode, 'float', '')
                me.execCommand(
                  'insertHtml',
                  '<p id="_img_parent_tmp" style="text-align:center">' + pN.innerHTML + '</p>'
                )
                tmpNode = me.document.getElementById('_img_parent_tmp')
                tmpNode.removeAttribute('id')
                tmpNode = tmpNode.firstChild
                range.selectNode(tmpNode).select()
                next = tmpNode.parentNode.nextSibling
                if (next && domUtils.isEmptyNode(next)) {
                  domUtils.remove(next)
                }
              }
              break
          }
        }
      }
    },
    queryCommandValue: function() {
      var range = this.selection.getRange()
      var startNode
      var floatStyle
      if (range.collapsed) {
        return 'none'
      }
      startNode = range.getClosedNode()
      if (startNode && startNode.nodeType == 1 && startNode.tagName == 'IMG') {
        floatStyle = domUtils.getComputedStyle(startNode, 'float') || startNode.getAttribute('align')
        if (floatStyle == 'none') {
          floatStyle = domUtils.getComputedStyle(startNode.parentNode, 'text-align') == 'center' ? 'center' : floatStyle
        }
        return { left: 1, right: 1, center: 1 }[floatStyle] ? floatStyle : 'none'
      }
      return 'none'
    },
    queryCommandState: function() {
      var range = this.selection.getRange()
      var startNode
      if (range.collapsed) {
        return -1
      }
      startNode = range.getClosedNode()
      if (startNode && startNode.nodeType == 1 && startNode.tagName == 'IMG') {
        return 0
      }
      return -1
    }
  }
  UE.commands['insertimage'] = {
    execCommand: function(cmd, opt) {
      opt = utils.isArray(opt) ? opt : [opt]
      if (!opt.length) {
        return
      }
      var me = this
      var range = me.selection.getRange()
      var img = range.getClosedNode()
      if (me.fireEvent('beforeinsertimage', opt) === true) {
        return
      }

      function unhtmlData(imgCi) {
        utils.each('width,height,border,hspace,vspace'.split(','), function(item) {
          if (imgCi[item]) {
            imgCi[item] = parseInt(imgCi[item], 10) || 0
          }
        })
        utils.each('src,_src'.split(','), function(item) {
          if (imgCi[item]) {
            imgCi[item] = utils.unhtmlForUrl(imgCi[item])
          }
        })
        utils.each('title,alt'.split(','), function(item) {
          if (imgCi[item]) {
            imgCi[item] = utils.unhtml(imgCi[item])
          }
        })
      }

      if (
        img &&
        /img/i.test(img.tagName) &&
        (img.className != 'edui-faked-video' || img.className.indexOf('edui-upload-video') != -1) &&
        !img.getAttribute('word_img')
      ) {
        var first = opt.shift()
        var floatStyle = first['floatStyle']
        delete first['floatStyle']
        domUtils.setAttributes(img, first)
        me.execCommand('imagefloat', floatStyle)
        if (opt.length > 0) {
          range.setStartAfter(img).setCursor(false, true)
          me.execCommand('insertimage', opt)
        }
      } else {
        var html = []
        var str = ''
        var ci
        ci = opt[0]
        if (opt.length == 1) {
          unhtmlData(ci)
          str =
            '<img src="' +
            ci.src +
            '" ' +
            (ci._src ? ' _src="' + ci._src + '" ' : '') +
            (ci.width ? 'width="' + ci.width + '" ' : '') +
            (ci.height ? ' height="' + ci.height + '" ' : '') +
            (ci['floatStyle'] == 'left' || ci['floatStyle'] == 'right'
              ? ' style="float:' + ci['floatStyle'] + ';"'
              : '') +
            (ci.title && ci.title != '' ? ' title="' + ci.title + '"' : '') +
            (ci.border && ci.border != '0' ? ' border="' + ci.border + '"' : '') +
            (ci.alt && ci.alt != '' ? ' alt="' + ci.alt + '"' : '') +
            (ci.hspace && ci.hspace != '0' ? ' hspace = "' + ci.hspace + '"' : '') +
            (ci.vspace && ci.vspace != '0' ? ' vspace = "' + ci.vspace + '"' : '') +
            '/>'
          if (ci['floatStyle'] == 'center') {
            str = '<p style="text-align: center">' + str + '</p>'
          }
          html.push(str)
        } else {
          for (var i = 0; (ci = opt[i++]);) {
            unhtmlData(ci)
            str =
              '<p ' +
              (ci['floatStyle'] == 'center' ? 'style="text-align: center" ' : '') +
              '><img src="' +
              ci.src +
              '" ' +
              (ci.width ? 'width="' + ci.width + '" ' : '') +
              (ci._src ? ' _src="' + ci._src + '" ' : '') +
              (ci.height ? ' height="' + ci.height + '" ' : '') +
              ' style="' +
              (ci['floatStyle'] && ci['floatStyle'] != 'center' ? 'float:' + ci['floatStyle'] + ';' : '') +
              (ci.border || '') +
              '" ' +
              (ci.title ? ' title="' + ci.title + '"' : '') +
              ' /></p>'
            html.push(str)
          }
        }
        me.execCommand('insertHtml', html.join(''))
      }
      me.fireEvent('afterinsertimage', opt)
    }
  }
  UE.plugins['justify'] = function() {
    var me = this
    var block = domUtils.isBlockElm
    var defaultValue = { left: 1, right: 1, center: 1, justify: 1 }
    var doJustify = function(range, style) {
      var bookmark = range.createBookmark()
      var filterFn = function(node) {
        return node.nodeType == 1
          ? node.tagName.toLowerCase() != 'br' && !domUtils.isBookmarkNode(node)
          : !domUtils.isWhitespace(node)
      }
      range.enlarge(true)
      var bookmark2 = range.createBookmark()
      var current = domUtils.getNextDomNode(bookmark2.start, false, filterFn)
      var tmpRange = range.cloneRange()
      var tmpNode
      while (current && !(domUtils.getPosition(current, bookmark2.end) & domUtils.POSITION_FOLLOWING)) {
        if (current.nodeType == 3 || !block(current)) {
          tmpRange.setStartBefore(current)
          while (current && current !== bookmark2.end && !block(current)) {
            tmpNode = current
            current = domUtils.getNextDomNode(current, false, null, function(node) {
              return !block(node)
            })
          }
          tmpRange.setEndAfter(tmpNode)
          var common = tmpRange.getCommonAncestor()
          if (!domUtils.isBody(common) && block(common)) {
            domUtils.setStyles(common, utils.isString(style) ? { 'text-align': style } : style)
            current = common
          } else {
            var p = range.document.createElement('p')
            domUtils.setStyles(p, utils.isString(style) ? { 'text-align': style } : style)
            var frag = tmpRange.extractContents()
            p.appendChild(frag)
            tmpRange.insertNode(p)
            current = p
          }
          current = domUtils.getNextDomNode(current, false, filterFn)
        } else {
          current = domUtils.getNextDomNode(current, true, filterFn)
        }
      }
      return range.moveToBookmark(bookmark2).moveToBookmark(bookmark)
    }
    UE.commands['justify'] = {
      execCommand: function(cmdName, align) {
        var range = this.selection.getRange()
        var txt
        if (range.collapsed) {
          txt = this.document.createTextNode('p')
          range.insertNode(txt)
        }
        doJustify(range, align)
        if (txt) {
          range.setStartBefore(txt).collapse(true)
          domUtils.remove(txt)
        }
        range.select()
        return true
      },
      queryCommandValue: function() {
        var startNode = this.selection.getStart()
        var value = domUtils.getComputedStyle(startNode, 'text-align')
        return defaultValue[value] ? value : 'left'
      },
      queryCommandState: function() {
        var start = this.selection.getStart()
        var cell = start && domUtils.findParentByTagName(start, ['td', 'th', 'caption'], true)
        return cell ? -1 : 0
      }
    }
  }
  UE.plugins['font'] = function() {
    var me = this
    var fonts = {
      forecolor: 'color',
      backcolor: 'background-color',
      fontsize: 'font-size',
      fontfamily: 'font-family',
      underline: 'text-decoration',
      strikethrough: 'text-decoration',
      fontborder: 'border'
    }
    var needCmd = { underline: 1, strikethrough: 1, fontborder: 1 }
    var needSetChild = {
      forecolor: 'color',
      backcolor: 'background-color',
      fontsize: 'font-size',
      fontfamily: 'font-family'
    }
    me.setOpt({
      fontfamily: [
        { name: 'songti', val: '宋体,SimSun' },
        { name: 'yahei', val: '微软雅黑,Microsoft YaHei' },
        { name: 'kaiti', val: '楷体,楷体_GB2312, SimKai' },
        { name: 'heiti', val: '黑体, SimHei' },
        { name: 'lishu', val: '隶书, SimLi' },
        { name: 'andaleMono', val: 'andale mono' },
        { name: 'arial', val: 'arial, helvetica,sans-serif' },
        { name: 'arialBlack', val: 'arial black,avant garde' },
        { name: 'comicSansMs', val: 'comic sans ms' },
        { name: 'impact', val: 'impact,chicago' },
        { name: 'timesNewRoman', val: 'times new roman' }
      ],
      fontsize: [10, 11, 12, 14, 16, 18, 20, 24, 36]
    })

    function mergeWithParent(node) {
      var parent
      while ((parent = node.parentNode)) {
        if (
          parent.tagName == 'SPAN' &&
          domUtils.getChildCount(parent, function(child) {
            return !domUtils.isBookmarkNode(child) && !domUtils.isBr(child)
          }) == 1
        ) {
          parent.style.cssText += node.style.cssText
          domUtils.remove(node, true)
          node = parent
        } else {
          break
        }
      }
    }

    function mergeChild(rng, cmdName, value) {
      if (needSetChild[cmdName]) {
        rng.adjustmentBoundary()
        if (!rng.collapsed && rng.startContainer.nodeType == 1) {
          var start = rng.startContainer.childNodes[rng.startOffset]
          if (start && domUtils.isTagNode(start, 'span')) {
            var bk = rng.createBookmark()
            utils.each(domUtils.getElementsByTagName(start, 'span'), function(span) {
              if (!span.parentNode || domUtils.isBookmarkNode(span)) {
                return
              }
              if (
                cmdName == 'backcolor' &&
                domUtils.getComputedStyle(span, 'background-color').toLowerCase() === value
              ) {
                return
              }
              domUtils.removeStyle(span, needSetChild[cmdName])
              if (span.style.cssText.replace(/^\s+$/, '').length == 0) {
                domUtils.remove(span, true)
              }
            })
            rng.moveToBookmark(bk)
          }
        }
      }
    }

    function mergesibling(rng, cmdName, value) {
      var collapsed = rng.collapsed
      var bk = rng.createBookmark()
      var common
      if (collapsed) {
        common = bk.start.parentNode
        while (dtd.$inline[common.tagName]) {
          common = common.parentNode
        }
      } else {
        common = domUtils.getCommonAncestor(bk.start, bk.end)
      }
      utils.each(domUtils.getElementsByTagName(common, 'span'), function(span) {
        if (!span.parentNode || domUtils.isBookmarkNode(span)) {
          return
        }
        if (/\s*border\s*:\s*none;?\s*/i.test(span.style.cssText)) {
          if (/^\s*border\s*:\s*none;?\s*$/.test(span.style.cssText)) {
            domUtils.remove(span, true)
          } else {
            domUtils.removeStyle(span, 'border')
          }
          return
        }
        if (
          /border/i.test(span.style.cssText) &&
          span.parentNode.tagName == 'SPAN' &&
          /border/i.test(span.parentNode.style.cssText)
        ) {
          span.style.cssText = span.style.cssText.replace(/border[^:]*:[^;]+;?/gi, '')
        }
        if (!(cmdName == 'fontborder' && value == 'none')) {
          var next = span.nextSibling
          while (next && next.nodeType == 1 && next.tagName == 'SPAN') {
            if (domUtils.isBookmarkNode(next) && cmdName == 'fontborder') {
              span.appendChild(next)
              next = span.nextSibling
              continue
            }
            if (next.style.cssText == span.style.cssText) {
              domUtils.moveChild(next, span)
              domUtils.remove(next)
            }
            if (span.nextSibling === next) {
              break
            }
            next = span.nextSibling
          }
        }
        mergeWithParent(span)
        if (browser.ie && browser.version > 8) {
          var parent = domUtils.findParent(span, function(n) {
            return n.tagName == 'SPAN' && /background-color/.test(n.style.cssText)
          })
          if (parent && !/background-color/.test(span.style.cssText)) {
            span.style.backgroundColor = parent.style.backgroundColor
          }
        }
      })
      rng.moveToBookmark(bk)
      mergeChild(rng, cmdName, value)
    }

    me.addInputRule(function(root) {
      utils.each(root.getNodesByTagName('u s del font strike'), function(node) {
        if (node.tagName == 'font') {
          var cssStyle = []
          for (var p in node.attrs) {
            switch (p) {
              case 'size':
                cssStyle.push(
                  'font-size:' +
                  ({ '1': '10', '2': '12', '3': '16', '4': '18', '5': '24', '6': '32', '7': '48' }[node.attrs[p]] ||
                    node.attrs[p]) +
                  'px'
                )
                break
              case 'color':
                cssStyle.push('color:' + node.attrs[p])
                break
              case 'face':
                cssStyle.push('font-family:' + node.attrs[p])
                break
              case 'style':
                cssStyle.push(node.attrs[p])
            }
          }
          node.attrs = { style: cssStyle.join(';') }
        } else {
          var val = node.tagName == 'u' ? 'underline' : 'line-through'
          node.attrs = { style: (node.getAttr('style') || '') + 'text-decoration:' + val + ';' }
        }
        node.tagName = 'span'
      })
    })
    for (var p in fonts) {
      ;(function(cmd, style) {
        UE.commands[cmd] = {
          execCommand: function(cmdName, value) {
            value =
              value ||
              (this.queryCommandState(cmdName)
                ? 'none'
                : cmdName == 'underline'
                  ? 'underline'
                  : cmdName == 'fontborder'
                    ? '1px solid #000'
                    : 'line-through')
            var me = this
            var range = this.selection.getRange()
            var text
            if (value == 'default') {
              if (range.collapsed) {
                text = me.document.createTextNode('font')
                range.insertNode(text).select()
              }
              me.execCommand('removeFormat', 'span,a', style)
              if (text) {
                range.setStartBefore(text).collapse(true)
                domUtils.remove(text)
              }
              mergesibling(range, cmdName, value)
              range.select()
            } else {
              if (!range.collapsed) {
                if (needCmd[cmd] && me.queryCommandValue(cmd)) {
                  me.execCommand('removeFormat', 'span,a', style)
                }
                range = me.selection.getRange()
                range.applyInlineStyle('span', { style: style + ':' + value })
                mergesibling(range, cmdName, value)
                range.select()
              } else {
                var span = domUtils.findParentByTagName(range.startContainer, 'span', true)
                text = me.document.createTextNode('font')
                if (
                  span &&
                  !span.children.length &&
                  !span[browser.ie ? 'innerText' : 'textContent'].replace(fillCharReg, '').length
                ) {
                  range.insertNode(text)
                  if (needCmd[cmd]) {
                    range.selectNode(text).select()
                    me.execCommand('removeFormat', 'span,a', style, null)
                    span = domUtils.findParentByTagName(text, 'span', true)
                    range.setStartBefore(text)
                  }
                  span && (span.style.cssText += ';' + style + ':' + value)
                  range.collapse(true).select()
                } else {
                  range.insertNode(text)
                  range.selectNode(text).select()
                  span = range.document.createElement('span')
                  if (needCmd[cmd]) {
                    if (domUtils.findParentByTagName(text, 'a', true)) {
                      range.setStartBefore(text).setCursor()
                      domUtils.remove(text)
                      return
                    }
                    me.execCommand('removeFormat', 'span,a', style)
                  }
                  span.style.cssText = style + ':' + value
                  text.parentNode.insertBefore(span, text)
                  if (!browser.ie || (browser.ie && browser.version == 9)) {
                    var spanParent = span.parentNode
                    while (!domUtils.isBlockElm(spanParent)) {
                      if (spanParent.tagName == 'SPAN') {
                        span.style.cssText = spanParent.style.cssText + ';' + span.style.cssText
                      }
                      spanParent = spanParent.parentNode
                    }
                  }
                  if (opera) {
                    setTimeout(function() {
                      range.setStart(span, 0).collapse(true)
                      mergesibling(range, cmdName, value)
                      range.select()
                    })
                  } else {
                    range.setStart(span, 0).collapse(true)
                    mergesibling(range, cmdName, value)
                    range.select()
                  }
                }
                domUtils.remove(text)
              }
            }
            return true
          },
          queryCommandValue: function(cmdName) {
            var startNode = this.selection.getStart()
            if (cmdName == 'underline' || cmdName == 'strikethrough') {
              var tmpNode = startNode
              var value
              while (tmpNode && !domUtils.isBlockElm(tmpNode) && !domUtils.isBody(tmpNode)) {
                if (tmpNode.nodeType == 1) {
                  value = domUtils.getComputedStyle(tmpNode, style)
                  if (value != 'none') {
                    return value
                  }
                }
                tmpNode = tmpNode.parentNode
              }
              return 'none'
            }
            if (cmdName == 'fontborder') {
              var tmp = startNode
              var val
              while (tmp && dtd.$inline[tmp.tagName]) {
                if ((val = domUtils.getComputedStyle(tmp, 'border'))) {
                  if (/1px/.test(val) && /solid/.test(val)) {
                    return val
                  }
                }
                tmp = tmp.parentNode
              }
              return ''
            }
            if (cmdName == 'FontSize') {
              var styleVal = domUtils.getComputedStyle(startNode, style)
              var tmp = /^([\d\.]+)(\w+)$/.exec(styleVal)
              if (tmp) {
                return Math.floor(tmp[1]) + tmp[2]
              }
              return styleVal
            }
            return domUtils.getComputedStyle(startNode, style)
          },
          queryCommandState: function(cmdName) {
            if (!needCmd[cmdName]) {
              return 0
            }
            var val = this.queryCommandValue(cmdName)
            if (cmdName == 'fontborder') {
              return /1px/.test(val) && /solid/.test(val)
            } else {
              return cmdName == 'underline' ? /underline/.test(val) : /line\-through/.test(val)
            }
          }
        }
      })(p, fonts[p])
    }
  }
  UE.plugins['link'] = function() {
    function optimize(range) {
      var start = range.startContainer
      var end = range.endContainer
      if ((start = domUtils.findParentByTagName(start, 'a', true))) {
        range.setStartBefore(start)
      }
      if ((end = domUtils.findParentByTagName(end, 'a', true))) {
        range.setEndAfter(end)
      }
    }

    UE.commands['unlink'] = {
      execCommand: function() {
        var range = this.selection.getRange()
        var bookmark
        if (range.collapsed && !domUtils.findParentByTagName(range.startContainer, 'a', true)) {
          return
        }
        bookmark = range.createBookmark()
        optimize(range)
        range
          .removeInlineStyle('a')
          .moveToBookmark(bookmark)
          .select()
      },
      queryCommandState: function() {
        return !this.highlight && this.queryCommandValue('link') ? 0 : -1
      }
    }

    function doLink(range, opt, me) {
      var rngClone = range.cloneRange()
      var link = me.queryCommandValue('link')
      optimize((range = range.adjustmentBoundary()))
      var start = range.startContainer
      if (start.nodeType == 1 && link) {
        start = start.childNodes[range.startOffset]
        if (
          start &&
          start.nodeType == 1 &&
          start.tagName == 'A' &&
          /^(?:https?|ftp|file)\s*:\s*\/\//.test(start[browser.ie ? 'innerText' : 'textContent'])
        ) {
          start[browser.ie ? 'innerText' : 'textContent'] = utils.html(opt.textValue || opt.href)
        }
      }
      if (!rngClone.collapsed || link) {
        range.removeInlineStyle('a')
        rngClone = range.cloneRange()
      }
      if (rngClone.collapsed) {
        var a = range.document.createElement('a')
        var text = ''
        if (opt.textValue) {
          text = utils.html(opt.textValue)
          delete opt.textValue
        } else {
          text = utils.html(opt.href)
        }
        domUtils.setAttributes(a, opt)
        start = domUtils.findParentByTagName(rngClone.startContainer, 'a', true)
        if (start && domUtils.isInNodeEndBoundary(rngClone, start)) {
          range.setStartAfter(start).collapse(true)
        }
        a[browser.ie ? 'innerText' : 'textContent'] = text
        range.insertNode(a).selectNode(a)
      } else {
        range.applyInlineStyle('a', opt)
      }
    }

    UE.commands['link'] = {
      execCommand: function(cmdName, opt) {
        var range
        opt._href && (opt._href = utils.unhtml(opt._href, /[<">]/g))
        opt.href && (opt.href = utils.unhtml(opt.href, /[<">]/g))
        opt.textValue && (opt.textValue = utils.unhtml(opt.textValue, /[<">]/g))
        doLink((range = this.selection.getRange()), opt, this)
        range.collapse().select(true)
      },
      queryCommandValue: function() {
        var range = this.selection.getRange()
        var node
        if (range.collapsed) {
          node = range.startContainer
          node = node.nodeType == 1 ? node : node.parentNode
          if (
            node &&
            (node = domUtils.findParentByTagName(node, 'a', true)) &&
            !domUtils.isInNodeEndBoundary(range, node)
          ) {
            return node
          }
        } else {
          range.shrinkBoundary()
          var start =
            range.startContainer.nodeType == 3 || !range.startContainer.childNodes[range.startOffset]
              ? range.startContainer
              : range.startContainer.childNodes[range.startOffset]
          var end =
            range.endContainer.nodeType == 3 || range.endOffset == 0
              ? range.endContainer
              : range.endContainer.childNodes[range.endOffset - 1]
          var common = range.getCommonAncestor()
          node = domUtils.findParentByTagName(common, 'a', true)
          if (!node && common.nodeType == 1) {
            var as = common.getElementsByTagName('a')
            var ps
            var pe
            for (var i = 0, ci; (ci = as[i++]);) {
              ;(ps = domUtils.getPosition(ci, start)), (pe = domUtils.getPosition(ci, end))
              if (
                (ps & domUtils.POSITION_FOLLOWING || ps & domUtils.POSITION_CONTAINS) &&
                (pe & domUtils.POSITION_PRECEDING || pe & domUtils.POSITION_CONTAINS)
              ) {
                node = ci
                break
              }
            }
          }
          return node
        }
      },
      queryCommandState: function() {
        var img = this.selection.getRange().getClosedNode()
        var flag = img && (img.className == 'edui-faked-video' || img.className.indexOf('edui-upload-video') != -1)
        return flag ? -1 : 0
      }
    }
  }
  UE.plugins['insertframe'] = function() {
    var me = this

    function deleteIframe() {
      me._iframe && delete me._iframe
    }

    me.addListener('selectionchange', function() {
      deleteIframe()
    })
  }
  UE.commands['scrawl'] = {
    queryCommandState: function() {
      return browser.ie && browser.version <= 8 ? -1 : 0
    }
  }
  UE.plugins['removeformat'] = function() {
    var me = this
    me.setOpt({
      removeFormatTags: 'b,big,code,del,dfn,em,font,i,ins,kbd,q,samp,small,span,strike,strong,sub,sup,tt,u,var',
      removeFormatAttributes: 'class,style,lang,width,height,align,hspace,valign'
    })
    me.commands['removeformat'] = {
      execCommand: function(cmdName, tags, style, attrs, notIncludeA) {
        var tagReg = new RegExp('^(?:' + (tags || this.options.removeFormatTags).replace(/,/g, '|') + ')$', 'i')
        var removeFormatAttributes = style ? [] : (attrs || this.options.removeFormatAttributes).split(',')
        var range = new dom.Range(this.document)
        var bookmark
        var node
        var parent
        var filter = function(node) {
          return node.nodeType == 1
        }

        function isRedundantSpan(node) {
          if (node.nodeType == 3 || node.tagName.toLowerCase() != 'span') {
            return 0
          }
          if (browser.ie) {
            var attrs = node.attributes
            if (attrs.length) {
              for (var i = 0, l = attrs.length; i < l; i++) {
                if (attrs[i].specified) {
                  return 0
                }
              }
              return 1
            }
          }
          return !node.attributes.length
        }

        function doRemove(range) {
          var bookmark1 = range.createBookmark()
          if (range.collapsed) {
            range.enlarge(true)
          }
          if (!notIncludeA) {
            var aNode = domUtils.findParentByTagName(range.startContainer, 'a', true)
            if (aNode) {
              range.setStartBefore(aNode)
            }
            aNode = domUtils.findParentByTagName(range.endContainer, 'a', true)
            if (aNode) {
              range.setEndAfter(aNode)
            }
          }
          bookmark = range.createBookmark()
          node = bookmark.start
          while ((parent = node.parentNode) && !domUtils.isBlockElm(parent)) {
            domUtils.breakParent(node, parent)
            domUtils.clearEmptySibling(node)
          }
          if (bookmark.end) {
            node = bookmark.end
            while ((parent = node.parentNode) && !domUtils.isBlockElm(parent)) {
              domUtils.breakParent(node, parent)
              domUtils.clearEmptySibling(node)
            }
            var current = domUtils.getNextDomNode(bookmark.start, false, filter)
            var next
            while (current) {
              if (current == bookmark.end) {
                break
              }
              next = domUtils.getNextDomNode(current, true, filter)
              if (!dtd.$empty[current.tagName.toLowerCase()] && !domUtils.isBookmarkNode(current)) {
                if (tagReg.test(current.tagName)) {
                  if (style) {
                    domUtils.removeStyle(current, style)
                    if (isRedundantSpan(current) && style != 'text-decoration') {
                      domUtils.remove(current, true)
                    }
                  } else {
                    domUtils.remove(current, true)
                  }
                } else {
                  if (!dtd.$tableContent[current.tagName] && !dtd.$list[current.tagName]) {
                    domUtils.removeAttributes(current, removeFormatAttributes)
                    if (isRedundantSpan(current)) {
                      domUtils.remove(current, true)
                    }
                  }
                }
              }
              current = next
            }
          }
          var pN = bookmark.start.parentNode
          if (domUtils.isBlockElm(pN) && !dtd.$tableContent[pN.tagName] && !dtd.$list[pN.tagName]) {
            domUtils.removeAttributes(pN, removeFormatAttributes)
          }
          pN = bookmark.end.parentNode
          if (bookmark.end && domUtils.isBlockElm(pN) && !dtd.$tableContent[pN.tagName] && !dtd.$list[pN.tagName]) {
            domUtils.removeAttributes(pN, removeFormatAttributes)
          }
          range.moveToBookmark(bookmark).moveToBookmark(bookmark1)
          var node = range.startContainer
          var tmp
          var collapsed = range.collapsed
          while (node.nodeType == 1 && domUtils.isEmptyNode(node) && dtd.$removeEmpty[node.tagName]) {
            tmp = node.parentNode
            range.setStartBefore(node)
            if (range.startContainer === range.endContainer) {
              range.endOffset--
            }
            domUtils.remove(node)
            node = tmp
          }
          if (!collapsed) {
            node = range.endContainer
            while (node.nodeType == 1 && domUtils.isEmptyNode(node) && dtd.$removeEmpty[node.tagName]) {
              tmp = node.parentNode
              range.setEndBefore(node)
              domUtils.remove(node)
              node = tmp
            }
          }
        }

        range = this.selection.getRange()
        doRemove(range)
        range.select()
      }
    }
  }
  UE.plugins['blockquote'] = function() {
    var me = this

    function getObj(editor) {
      return domUtils.filterNodeList(editor.selection.getStartElementPath(), 'blockquote')
    }

    me.commands['blockquote'] = {
      execCommand: function(cmdName, attrs) {
        var range = this.selection.getRange()
        var obj = getObj(this)
        var blockquote = dtd.blockquote
        var bookmark = range.createBookmark()
        if (obj) {
          var start = range.startContainer
          var startBlock = domUtils.isBlockElm(start)
            ? start
            : domUtils.findParent(start, function(node) {
              return domUtils.isBlockElm(node)
            })
          var end = range.endContainer
          var endBlock = domUtils.isBlockElm(end)
            ? end
            : domUtils.findParent(end, function(node) {
              return domUtils.isBlockElm(node)
            })
          startBlock = domUtils.findParentByTagName(startBlock, 'li', true) || startBlock
          endBlock = domUtils.findParentByTagName(endBlock, 'li', true) || endBlock
          if (
            startBlock.tagName == 'LI' ||
            startBlock.tagName == 'TD' ||
            startBlock === obj ||
            domUtils.isBody(startBlock)
          ) {
            domUtils.remove(obj, true)
          } else {
            domUtils.breakParent(startBlock, obj)
          }
          if (startBlock !== endBlock) {
            obj = domUtils.findParentByTagName(endBlock, 'blockquote')
            if (obj) {
              if (endBlock.tagName == 'LI' || endBlock.tagName == 'TD' || domUtils.isBody(endBlock)) {
                obj.parentNode && domUtils.remove(obj, true)
              } else {
                domUtils.breakParent(endBlock, obj)
              }
            }
          }
          var blockquotes = domUtils.getElementsByTagName(this.document, 'blockquote')
          for (var i = 0, bi; (bi = blockquotes[i++]);) {
            if (!bi.childNodes.length) {
              domUtils.remove(bi)
            } else {
              if (
                domUtils.getPosition(bi, startBlock) & domUtils.POSITION_FOLLOWING &&
                domUtils.getPosition(bi, endBlock) & domUtils.POSITION_PRECEDING
              ) {
                domUtils.remove(bi, true)
              }
            }
          }
        } else {
          var tmpRange = range.cloneRange()
          var node =
            tmpRange.startContainer.nodeType == 1 ? tmpRange.startContainer : tmpRange.startContainer.parentNode
          var preNode = node
          var doEnd = 1
          while (1) {
            if (domUtils.isBody(node)) {
              if (preNode !== node) {
                if (range.collapsed) {
                  tmpRange.selectNode(preNode)
                  doEnd = 0
                } else {
                  tmpRange.setStartBefore(preNode)
                }
              } else {
                tmpRange.setStart(node, 0)
              }
              break
            }
            if (!blockquote[node.tagName]) {
              if (range.collapsed) {
                tmpRange.selectNode(preNode)
              } else {
                tmpRange.setStartBefore(preNode)
              }
              break
            }
            preNode = node
            node = node.parentNode
          }
          if (doEnd) {
            preNode = node = node =
              tmpRange.endContainer.nodeType == 1 ? tmpRange.endContainer : tmpRange.endContainer.parentNode
            while (1) {
              if (domUtils.isBody(node)) {
                if (preNode !== node) {
                  tmpRange.setEndAfter(preNode)
                } else {
                  tmpRange.setEnd(node, node.childNodes.length)
                }
                break
              }
              if (!blockquote[node.tagName]) {
                tmpRange.setEndAfter(preNode)
                break
              }
              preNode = node
              node = node.parentNode
            }
          }
          node = range.document.createElement('blockquote')
          domUtils.setAttributes(node, attrs)
          node.appendChild(tmpRange.extractContents())
          tmpRange.insertNode(node)
          var childs = domUtils.getElementsByTagName(node, 'blockquote')
          for (var i = 0, ci; (ci = childs[i++]);) {
            if (ci.parentNode) {
              domUtils.remove(ci, true)
            }
          }
        }
        range.moveToBookmark(bookmark).select()
      },
      queryCommandState: function() {
        return getObj(this) ? 1 : 0
      }
    }
  }
  UE.commands['touppercase'] = UE.commands['tolowercase'] = {
    execCommand: function(cmd) {
      var me = this
      var rng = me.selection.getRange()
      if (rng.collapsed) {
        return rng
      }
      var bk = rng.createBookmark()
      var bkEnd = bk.end
      var filterFn = function(node) {
        return !domUtils.isBr(node) && !domUtils.isWhitespace(node)
      }
      var curNode = domUtils.getNextDomNode(bk.start, false, filterFn)
      while (curNode && domUtils.getPosition(curNode, bkEnd) & domUtils.POSITION_PRECEDING) {
        if (curNode.nodeType == 3) {
          curNode.nodeValue = curNode.nodeValue[cmd == 'touppercase' ? 'toUpperCase' : 'toLowerCase']()
        }
        curNode = domUtils.getNextDomNode(curNode, true, filterFn)
        if (curNode === bkEnd) {
          break
        }
      }
      rng.moveToBookmark(bk).select()
    }
  }
  UE.commands['indent'] = {
    execCommand: function() {
      var me = this
      var value = me.queryCommandState('indent') ? '0em' : me.options.indentValue || '2em'
      me.execCommand('Paragraph', 'p', { style: 'text-indent:' + value })
    },
    queryCommandState: function() {
      var pN = domUtils.filterNodeList(this.selection.getStartElementPath(), 'p h1 h2 h3 h4 h5 h6')
      return pN && pN.style.textIndent && parseInt(pN.style.textIndent) ? 1 : 0
    }
  }
  UE.commands['print'] = {
    execCommand: function() {
      this.window.print()
    },
    notNeedUndo: 1
  }
  UE.commands['preview'] = {
    execCommand: function() {
      var w = window.open('', '_blank', '')
      var d = w.document
      d.open()
      d.write(
        '<!DOCTYPE html><html><head><meta charset="utf-8"/><script src="' +
        this.options.UEDITOR_HOME_URL +
        'ueditor.parse.js"></script><script>' +
        'setTimeout(function(){uParse(\'div\',{rootPath: \'' +
        this.options.UEDITOR_HOME_URL +
        '\'})},300)' +
        '</script></head><body><div>' +
        this.getContent(null, null, true) +
        '</div></body></html>'
      )
      d.close()
    },
    notNeedUndo: 1
  }
  UE.plugins['selectall'] = function() {
    var me = this
    me.commands['selectall'] = {
      execCommand: function() {
        var me = this
        var body = me.body
        var range = me.selection.getRange()
        range.selectNodeContents(body)
        if (domUtils.isEmptyBlock(body)) {
          if (browser.opera && body.firstChild && body.firstChild.nodeType == 1) {
            range.setStartAtFirst(body.firstChild)
          }
          range.collapse(true)
        }
        range.select(true)
      },
      notNeedUndo: 1
    }
    me.addshortcutkey({ selectAll: 'ctrl+65' })
  }
  UE.plugins['paragraph'] = function() {
    var me = this
    var block = domUtils.isBlockElm
    var notExchange = ['TD', 'LI', 'PRE']
    var doParagraph = function(range, style, attrs, sourceCmdName) {
      var bookmark = range.createBookmark()
      var filterFn = function(node) {
        return node.nodeType == 1
          ? node.tagName.toLowerCase() != 'br' && !domUtils.isBookmarkNode(node)
          : !domUtils.isWhitespace(node)
      }
      var para
      range.enlarge(true)
      var bookmark2 = range.createBookmark()
      var current = domUtils.getNextDomNode(bookmark2.start, false, filterFn)
      var tmpRange = range.cloneRange()
      var tmpNode
      while (current && !(domUtils.getPosition(current, bookmark2.end) & domUtils.POSITION_FOLLOWING)) {
        if (current.nodeType == 3 || !block(current)) {
          tmpRange.setStartBefore(current)
          while (current && current !== bookmark2.end && !block(current)) {
            tmpNode = current
            current = domUtils.getNextDomNode(current, false, null, function(node) {
              return !block(node)
            })
          }
          tmpRange.setEndAfter(tmpNode)
          para = range.document.createElement(style)
          if (attrs) {
            domUtils.setAttributes(para, attrs)
            if (sourceCmdName && sourceCmdName == 'customstyle' && attrs.style) {
              para.style.cssText = attrs.style
            }
          }
          para.appendChild(tmpRange.extractContents())
          if (domUtils.isEmptyNode(para)) {
            domUtils.fillChar(range.document, para)
          }
          tmpRange.insertNode(para)
          var parent = para.parentNode
          if (block(parent) && !domUtils.isBody(para.parentNode) && utils.indexOf(notExchange, parent.tagName) == -1) {
            if (!(sourceCmdName && sourceCmdName == 'customstyle')) {
              parent.getAttribute('dir') && para.setAttribute('dir', parent.getAttribute('dir'))
              parent.style.cssText && (para.style.cssText = parent.style.cssText + ';' + para.style.cssText)
              parent.style.textAlign && !para.style.textAlign && (para.style.textAlign = parent.style.textAlign)
              parent.style.textIndent && !para.style.textIndent && (para.style.textIndent = parent.style.textIndent)
              parent.style.padding && !para.style.padding && (para.style.padding = parent.style.padding)
            }
            if (attrs && /h\d/i.test(parent.tagName) && !/h\d/i.test(para.tagName)) {
              domUtils.setAttributes(parent, attrs)
              if (sourceCmdName && sourceCmdName == 'customstyle' && attrs.style) {
                parent.style.cssText = attrs.style
              }
              domUtils.remove(para, true)
              para = parent
            } else {
              domUtils.remove(para.parentNode, true)
            }
          }
          if (utils.indexOf(notExchange, parent.tagName) != -1) {
            current = parent
          } else {
            current = para
          }
          current = domUtils.getNextDomNode(current, false, filterFn)
        } else {
          current = domUtils.getNextDomNode(current, true, filterFn)
        }
      }
      return range.moveToBookmark(bookmark2).moveToBookmark(bookmark)
    }
    me.setOpt('paragraph', { p: '', h1: '', h2: '', h3: '', h4: '', h5: '', h6: '' })
    me.commands['paragraph'] = {
      execCommand: function(cmdName, style, attrs, sourceCmdName) {
        var range = this.selection.getRange()
        if (range.collapsed) {
          var txt = this.document.createTextNode('p')
          range.insertNode(txt)
          if (browser.ie) {
            var node = txt.previousSibling
            if (node && domUtils.isWhitespace(node)) {
              domUtils.remove(node)
            }
            node = txt.nextSibling
            if (node && domUtils.isWhitespace(node)) {
              domUtils.remove(node)
            }
          }
        }
        range = doParagraph(range, style, attrs, sourceCmdName)
        if (txt) {
          range.setStartBefore(txt).collapse(true)
          pN = txt.parentNode
          domUtils.remove(txt)
          if (domUtils.isBlockElm(pN) && domUtils.isEmptyNode(pN)) {
            domUtils.fillNode(this.document, pN)
          }
        }
        if (browser.gecko && range.collapsed && range.startContainer.nodeType == 1) {
          var child = range.startContainer.childNodes[range.startOffset]
          if (child && child.nodeType == 1 && child.tagName.toLowerCase() == style) {
            range.setStart(child, 0).collapse(true)
          }
        }
        range.select()
        return true
      },
      queryCommandValue: function() {
        var node = domUtils.filterNodeList(this.selection.getStartElementPath(), 'p h1 h2 h3 h4 h5 h6')
        return node ? node.tagName.toLowerCase() : ''
      }
    }
  }
  ;(function() {
    var block = domUtils.isBlockElm
    var getObj = function(editor) {
      return domUtils.filterNodeList(editor.selection.getStartElementPath(), function(n) {
        return n && n.nodeType == 1 && n.getAttribute('dir')
      })
    }
    var doDirectionality = function(range, editor, forward) {
      var bookmark
      var filterFn = function(node) {
        return node.nodeType == 1 ? !domUtils.isBookmarkNode(node) : !domUtils.isWhitespace(node)
      }
      var obj = getObj(editor)
      if (obj && range.collapsed) {
        obj.setAttribute('dir', forward)
        return range
      }
      bookmark = range.createBookmark()
      range.enlarge(true)
      var bookmark2 = range.createBookmark()
      var current = domUtils.getNextDomNode(bookmark2.start, false, filterFn)
      var tmpRange = range.cloneRange()
      var tmpNode
      while (current && !(domUtils.getPosition(current, bookmark2.end) & domUtils.POSITION_FOLLOWING)) {
        if (current.nodeType == 3 || !block(current)) {
          tmpRange.setStartBefore(current)
          while (current && current !== bookmark2.end && !block(current)) {
            tmpNode = current
            current = domUtils.getNextDomNode(current, false, null, function(node) {
              return !block(node)
            })
          }
          tmpRange.setEndAfter(tmpNode)
          var common = tmpRange.getCommonAncestor()
          if (!domUtils.isBody(common) && block(common)) {
            common.setAttribute('dir', forward)
            current = common
          } else {
            var p = range.document.createElement('p')
            p.setAttribute('dir', forward)
            var frag = tmpRange.extractContents()
            p.appendChild(frag)
            tmpRange.insertNode(p)
            current = p
          }
          current = domUtils.getNextDomNode(current, false, filterFn)
        } else {
          current = domUtils.getNextDomNode(current, true, filterFn)
        }
      }
      return range.moveToBookmark(bookmark2).moveToBookmark(bookmark)
    }
    UE.commands['directionality'] = {
      execCommand: function(cmdName, forward) {
        var range = this.selection.getRange()
        if (range.collapsed) {
          var txt = this.document.createTextNode('d')
          range.insertNode(txt)
        }
        doDirectionality(range, this, forward)
        if (txt) {
          range.setStartBefore(txt).collapse(true)
          domUtils.remove(txt)
        }
        range.select()
        return true
      },
      queryCommandValue: function() {
        var node = getObj(this)
        return node ? node.getAttribute('dir') : 'ltr'
      }
    }
  })()
  UE.plugins['horizontal'] = function() {
    var me = this
    me.commands['horizontal'] = {
      execCommand: function(cmdName) {
        var me = this
        if (me.queryCommandState(cmdName) !== -1) {
          me.execCommand('insertHtml', '<hr>')
          var range = me.selection.getRange()
          var start = range.startContainer
          if (start.nodeType == 1 && !start.childNodes[range.startOffset]) {
            var tmp
            if ((tmp = start.childNodes[range.startOffset - 1])) {
              if (tmp.nodeType == 1 && tmp.tagName == 'HR') {
                if (me.options.enterTag == 'p') {
                  tmp = me.document.createElement('p')
                  range.insertNode(tmp)
                  range.setStart(tmp, 0).setCursor()
                } else {
                  tmp = me.document.createElement('br')
                  range.insertNode(tmp)
                  range.setStartBefore(tmp).setCursor()
                }
              }
            }
          }
          return true
        }
      },
      queryCommandState: function() {
        return domUtils.filterNodeList(this.selection.getStartElementPath(), 'table') ? -1 : 0
      }
    }
    me.addListener('delkeydown', function(name, evt) {
      var rng = this.selection.getRange()
      rng.txtToElmBoundary(true)
      if (domUtils.isStartInblock(rng)) {
        var tmpNode = rng.startContainer
        var pre = tmpNode.previousSibling
        if (pre && domUtils.isTagNode(pre, 'hr')) {
          domUtils.remove(pre)
          rng.select()
          domUtils.preventDefault(evt)
          return true
        }
      }
    })
  }
  UE.commands['time'] = UE.commands['date'] = {
    execCommand: function(cmd, format) {
      var date = new Date()

      function formatTime(date, format) {
        var hh = ('0' + date.getHours()).slice(-2)
        var ii = ('0' + date.getMinutes()).slice(-2)
        var ss = ('0' + date.getSeconds()).slice(-2)
        format = format || 'hh:ii:ss'
        return format
          .replace(/hh/gi, hh)
          .replace(/ii/gi, ii)
          .replace(/ss/gi, ss)
      }

      function formatDate(date, format) {
        var yyyy = ('000' + date.getFullYear()).slice(-4)
        var yy = yyyy.slice(-2)
        var mm = ('0' + (date.getMonth() + 1)).slice(-2)
        var dd = ('0' + date.getDate()).slice(-2)
        format = format || 'yyyy-mm-dd'
        return format
          .replace(/yyyy/gi, yyyy)
          .replace(/yy/gi, yy)
          .replace(/mm/gi, mm)
          .replace(/dd/gi, dd)
      }

      this.execCommand('insertHtml', cmd == 'time' ? formatTime(date, format) : formatDate(date, format))
    }
  }
  UE.plugins['rowspacing'] = function() {
    var me = this
    me.setOpt({ rowspacingtop: ['5', '10', '15', '20', '25'], rowspacingbottom: ['5', '10', '15', '20', '25'] })
    me.commands['rowspacing'] = {
      execCommand: function(cmdName, value, dir) {
        this.execCommand('paragraph', 'p', { style: 'margin-' + dir + ':' + value + 'px' })
        return true
      },
      queryCommandValue: function(cmdName, dir) {
        var pN = domUtils.filterNodeList(this.selection.getStartElementPath(), function(node) {
          return domUtils.isBlockElm(node)
        })
        var value
        if (pN) {
          value = domUtils.getComputedStyle(pN, 'margin-' + dir).replace(/[^\d]/g, '')
          return !value ? 0 : value
        }
        return 0
      }
    }
  }
  UE.plugins['lineheight'] = function() {
    var me = this
    me.setOpt({ lineheight: ['1', '1.5', '1.75', '2', '3', '4', '5'] })
    me.commands['lineheight'] = {
      execCommand: function(cmdName, value) {
        this.execCommand('paragraph', 'p', { style: 'line-height:' + (value == '1' ? 'normal' : value + 'em') })
        return true
      },
      queryCommandValue: function() {
        var pN = domUtils.filterNodeList(this.selection.getStartElementPath(), function(node) {
          return domUtils.isBlockElm(node)
        })
        if (pN) {
          var value = domUtils.getComputedStyle(pN, 'line-height')
          return value == 'normal' ? 1 : value.replace(/[^\d.]*/gi, '')
        }
      }
    }
  }
  UE.plugins['letterspacing'] = function() {
    var me = this
    me.setOpt({ letterspacing: ['0', '1.5', '1.75', '2', '3', '4', '5'] })
    me.commands['letterspacing'] = {
      execCommand: function(cmdName, value) {
        this.execCommand('paragraph', 'p', { style: 'letter-spacing:' + value + 'px' })
        return true
      },
      queryCommandValue: function() {
        var pN = domUtils.filterNodeList(this.selection.getStartElementPath(), function(node) {
          return domUtils.isBlockElm(node)
        })
        if (pN) {
          var value = domUtils.getComputedStyle(pN, 'letter-spacing')
          return value == value.replace(/[^\d.]*/gi, '')
        }
      }
    }
  }
  UE.plugins['insertcode'] = function() {
    var me = this
    me.ready(function() {
      utils.cssRule('pre', 'pre{margin:.5em 0;padding:.4em .6em;border-radius:8px;background:#f8f8f8;}', me.document)
    })
    me.setOpt('insertcode', {
      as3: 'ActionScript3',
      bash: 'Bash/Shell',
      cpp: 'C/C++',
      css: 'Css',
      cf: 'CodeFunction',
      'c#': 'C#',
      delphi: 'Delphi',
      diff: 'Diff',
      erlang: 'Erlang',
      groovy: 'Groovy',
      html: 'Html',
      java: 'Java',
      jfx: 'JavaFx',
      js: 'Javascript',
      pl: 'Perl',
      php: 'Php',
      plain: 'Plain Text',
      ps: 'PowerShell',
      python: 'Python',
      ruby: 'Ruby',
      scala: 'Scala',
      sql: 'Sql',
      vb: 'Vb',
      xml: 'Xml'
    })
    me.commands['insertcode'] = {
      execCommand: function(cmd, lang) {
        var me = this
        var rng = me.selection.getRange()
        var pre = domUtils.findParentByTagName(rng.startContainer, 'pre', true)
        if (pre) {
          pre.className = 'brush:' + lang + ';toolbar:false;'
        } else {
          var code = ''
          if (rng.collapsed) {
            code = browser.ie && browser.ie11below ? (browser.version <= 8 ? '&nbsp;' : '') : '<br/>'
          } else {
            var frag = rng.extractContents()
            var div = me.document.createElement('div')
            div.appendChild(frag)
            utils.each(
              UE.filterNode(UE.htmlparser(div.innerHTML.replace(/[\r\t]/g, '')), me.options.filterTxtRules).children,
              function(node) {
                if (browser.ie && browser.ie11below && browser.version > 8) {
                  if (node.type == 'element') {
                    if (node.tagName == 'br') {
                      code += '\n'
                    } else {
                      if (!dtd.$empty[node.tagName]) {
                        utils.each(node.children, function(cn) {
                          if (cn.type == 'element') {
                            if (cn.tagName == 'br') {
                              code += '\n'
                            } else {
                              if (!dtd.$empty[node.tagName]) {
                                code += cn.innerText()
                              }
                            }
                          } else {
                            code += cn.data
                          }
                        })
                        if (!/\n$/.test(code)) {
                          code += '\n'
                        }
                      }
                    }
                  } else {
                    code += node.data + '\n'
                  }
                  if (!node.nextSibling() && /\n$/.test(code)) {
                    code = code.replace(/\n$/, '')
                  }
                } else {
                  if (browser.ie && browser.ie11below) {
                    if (node.type == 'element') {
                      if (node.tagName == 'br') {
                        code += '<br>'
                      } else {
                        if (!dtd.$empty[node.tagName]) {
                          utils.each(node.children, function(cn) {
                            if (cn.type == 'element') {
                              if (cn.tagName == 'br') {
                                code += '<br>'
                              } else {
                                if (!dtd.$empty[node.tagName]) {
                                  code += cn.innerText()
                                }
                              }
                            } else {
                              code += cn.data
                            }
                          })
                          if (!/br>$/.test(code)) {
                            code += '<br>'
                          }
                        }
                      }
                    } else {
                      code += node.data + '<br>'
                    }
                    if (!node.nextSibling() && /<br>$/.test(code)) {
                      code = code.replace(/<br>$/, '')
                    }
                  } else {
                    code += node.type == 'element' ? (dtd.$empty[node.tagName] ? '' : node.innerText()) : node.data
                    if (!/br\/?\s*>$/.test(code)) {
                      if (!node.nextSibling()) {
                        return
                      }
                      code += '<br>'
                    }
                  }
                }
              }
            )
          }
          me.execCommand(
            'inserthtml',
            '<pre id="coder"class="brush:' + lang + ';toolbar:false">' + code + '</pre>',
            true
          )
          pre = me.document.getElementById('coder')
          domUtils.removeAttributes(pre, 'id')
          var tmpNode = pre.previousSibling
          if (
            tmpNode &&
            ((tmpNode.nodeType == 3 && tmpNode.nodeValue.length == 1 && browser.ie && browser.version == 6) ||
              domUtils.isEmptyBlock(tmpNode))
          ) {
            domUtils.remove(tmpNode)
          }
          var rng = me.selection.getRange()
          if (domUtils.isEmptyBlock(pre)) {
            rng.setStart(pre, 0).setCursor(false, true)
          } else {
            rng.selectNodeContents(pre).select()
          }
        }
      },
      queryCommandValue: function() {
        var path = this.selection.getStartElementPath()
        var lang = ''
        utils.each(path, function(node) {
          if (node.nodeName == 'PRE') {
            var match = node.className.match(/brush:([^;]+)/)
            lang = match && match[1] ? match[1] : ''
            return false
          }
        })
        return lang
      }
    }
    me.addInputRule(function(root) {
      utils.each(root.getNodesByTagName('pre'), function(pre) {
        var brs = pre.getNodesByTagName('br')
        if (brs.length) {
          browser.ie &&
          browser.ie11below &&
          browser.version > 8 &&
          utils.each(brs, function(br) {
            var txt = UE.uNode.createText('\n')
            br.parentNode.insertBefore(txt, br)
            br.parentNode.removeChild(br)
          })
          return
        }
        if (browser.ie && browser.ie11below && browser.version > 8) {
          return
        }
        var code = pre.innerText().split(/\n/)
        pre.innerHTML('')
        utils.each(code, function(c) {
          if (c.length) {
            pre.appendChild(UE.uNode.createText(c))
          }
          pre.appendChild(UE.uNode.createElement('br'))
        })
      })
    })
    me.addOutputRule(function(root) {
      utils.each(root.getNodesByTagName('pre'), function(pre) {
        var code = ''
        utils.each(pre.children, function(n) {
          if (n.type == 'text') {
            code += n.data.replace(/[ ]/g, '&nbsp;').replace(/\n$/, '')
          } else {
            if (n.tagName == 'br') {
              code += '\n'
            } else {
              code += !dtd.$empty[n.tagName] ? '' : n.innerText()
            }
          }
        })
        pre.innerText(code.replace(/(&nbsp;|\n)+$/, ''))
      })
    })
    me.notNeedCodeQuery = {
      help: 1,
      undo: 1,
      redo: 1,
      source: 1,
      print: 1,
      searchreplace: 1,
      fullscreen: 1,
      preview: 1,
      insertparagraph: 1,
      elementpath: 1,
      insertcode: 1,
      inserthtml: 1,
      selectall: 1
    }
    var orgQuery = me.queryCommandState
    me.queryCommandState = function(cmd) {
      var me = this
      if (!me.notNeedCodeQuery[cmd.toLowerCase()] && me.selection && me.queryCommandValue('insertcode')) {
        return -1
      }
      return UE.Editor.prototype.queryCommandState.apply(this, arguments)
    }
    me.addListener('beforeenterkeydown', function() {
      var rng = me.selection.getRange()
      var pre = domUtils.findParentByTagName(rng.startContainer, 'pre', true)
      if (pre) {
        me.fireEvent('saveScene')
        if (!rng.collapsed) {
          rng.deleteContents()
        }
        if (!browser.ie || browser.ie9above) {
          var tmpNode = me.document.createElement('br')
          var pre
          rng
            .insertNode(tmpNode)
            .setStartAfter(tmpNode)
            .collapse(true)
          var next = tmpNode.nextSibling
          if (!next && (!browser.ie || browser.version > 10)) {
            rng.insertNode(tmpNode.cloneNode(false))
          } else {
            rng.setStartAfter(tmpNode)
          }
          pre = tmpNode.previousSibling
          var tmp
          while (pre) {
            tmp = pre
            pre = pre.previousSibling
            if (!pre || pre.nodeName == 'BR') {
              pre = tmp
              break
            }
          }
          if (pre) {
            var str = ''
            while (pre && pre.nodeName != 'BR' && new RegExp('^[\\s' + domUtils.fillChar + ']*$').test(pre.nodeValue)) {
              str += pre.nodeValue
              pre = pre.nextSibling
            }
            if (pre.nodeName != 'BR') {
              var match = pre.nodeValue.match(new RegExp('^([\\s' + domUtils.fillChar + ']+)'))
              if (match && match[1]) {
                str += match[1]
              }
            }
            if (str) {
              str = me.document.createTextNode(str)
              rng.insertNode(str).setStartAfter(str)
            }
          }
          rng.collapse(true).select(true)
        } else {
          if (browser.version > 8) {
            var txt = me.document.createTextNode('\n')
            var start = rng.startContainer
            if (rng.startOffset == 0) {
              var preNode = start.previousSibling
              if (preNode) {
                rng.insertNode(txt)
                var fillchar = me.document.createTextNode(' ')
                rng
                  .setStartAfter(txt)
                  .insertNode(fillchar)
                  .setStart(fillchar, 0)
                  .collapse(true)
                  .select(true)
              }
            } else {
              rng.insertNode(txt).setStartAfter(txt)
              var fillchar = me.document.createTextNode(' ')
              start = rng.startContainer.childNodes[rng.startOffset]
              if (start && !/^\n/.test(start.nodeValue)) {
                rng.setStartBefore(txt)
              }
              rng
                .insertNode(fillchar)
                .setStart(fillchar, 0)
                .collapse(true)
                .select(true)
            }
          } else {
            var tmpNode = me.document.createElement('br')
            rng.insertNode(tmpNode)
            rng.insertNode(me.document.createTextNode(domUtils.fillChar))
            rng.setStartAfter(tmpNode)
            pre = tmpNode.previousSibling
            var tmp
            while (pre) {
              tmp = pre
              pre = pre.previousSibling
              if (!pre || pre.nodeName == 'BR') {
                pre = tmp
                break
              }
            }
            if (pre) {
              var str = ''
              while (pre && pre.nodeName != 'BR' && new RegExp('^[ ' + domUtils.fillChar + ']*$').test(pre.nodeValue)) {
                str += pre.nodeValue
                pre = pre.nextSibling
              }
              if (pre.nodeName != 'BR') {
                var match = pre.nodeValue.match(new RegExp('^([ ' + domUtils.fillChar + ']+)'))
                if (match && match[1]) {
                  str += match[1]
                }
              }
              str = me.document.createTextNode(str)
              rng.insertNode(str).setStartAfter(str)
            }
            rng.collapse(true).select()
          }
        }
        me.fireEvent('saveScene')
        return true
      }
    })
    me.addListener('tabkeydown', function(cmd, evt) {
      var rng = me.selection.getRange()
      var pre = domUtils.findParentByTagName(rng.startContainer, 'pre', true)
      if (pre) {
        me.fireEvent('saveScene')
        if (evt.shiftKey) {
        } else {
          if (!rng.collapsed) {
            var bk = rng.createBookmark()
            var start = bk.start.previousSibling
            while (start) {
              if (pre.firstChild === start && !domUtils.isBr(start)) {
                pre.insertBefore(me.document.createTextNode('    '), start)
                break
              }
              if (domUtils.isBr(start)) {
                pre.insertBefore(me.document.createTextNode('    '), start.nextSibling)
                break
              }
              start = start.previousSibling
            }
            var end = bk.end
            start = bk.start.nextSibling
            if (pre.firstChild === bk.start) {
              pre.insertBefore(me.document.createTextNode('    '), start.nextSibling)
            }
            while (start && start !== end) {
              if (domUtils.isBr(start) && start.nextSibling) {
                if (start.nextSibling === end) {
                  break
                }
                pre.insertBefore(me.document.createTextNode('    '), start.nextSibling)
              }
              start = start.nextSibling
            }
            rng.moveToBookmark(bk).select()
          } else {
            var tmpNode = me.document.createTextNode('    ')
            rng
              .insertNode(tmpNode)
              .setStartAfter(tmpNode)
              .collapse(true)
              .select(true)
          }
        }
        me.fireEvent('saveScene')
        return true
      }
    })
    me.addListener('beforeinserthtml', function(evtName, html) {
      var me = this
      var rng = me.selection.getRange()
      var pre = domUtils.findParentByTagName(rng.startContainer, 'pre', true)
      if (pre) {
        if (!rng.collapsed) {
          rng.deleteContents()
        }
        var htmlstr = ''
        if (browser.ie && browser.version > 8) {
          utils.each(UE.filterNode(UE.htmlparser(html), me.options.filterTxtRules).children, function(node) {
            if (node.type == 'element') {
              if (node.tagName == 'br') {
                htmlstr += '\n'
              } else {
                if (!dtd.$empty[node.tagName]) {
                  utils.each(node.children, function(cn) {
                    if (cn.type == 'element') {
                      if (cn.tagName == 'br') {
                        htmlstr += '\n'
                      } else {
                        if (!dtd.$empty[node.tagName]) {
                          htmlstr += cn.innerText()
                        }
                      }
                    } else {
                      htmlstr += cn.data
                    }
                  })
                  if (!/\n$/.test(htmlstr)) {
                    htmlstr += '\n'
                  }
                }
              }
            } else {
              htmlstr += node.data + '\n'
            }
            if (!node.nextSibling() && /\n$/.test(htmlstr)) {
              htmlstr = htmlstr.replace(/\n$/, '')
            }
          })
          var tmpNode = me.document.createTextNode(utils.html(htmlstr.replace(/&nbsp;/g, ' ')))
          rng
            .insertNode(tmpNode)
            .selectNode(tmpNode)
            .select()
        } else {
          var frag = me.document.createDocumentFragment()
          utils.each(UE.filterNode(UE.htmlparser(html), me.options.filterTxtRules).children, function(node) {
            if (node.type == 'element') {
              if (node.tagName == 'br') {
                frag.appendChild(me.document.createElement('br'))
              } else {
                if (!dtd.$empty[node.tagName]) {
                  utils.each(node.children, function(cn) {
                    if (cn.type == 'element') {
                      if (cn.tagName == 'br') {
                        frag.appendChild(me.document.createElement('br'))
                      } else {
                        if (!dtd.$empty[node.tagName]) {
                          frag.appendChild(
                            me.document.createTextNode(utils.html(cn.innerText().replace(/&nbsp;/g, ' ')))
                          )
                        }
                      }
                    } else {
                      frag.appendChild(me.document.createTextNode(utils.html(cn.data.replace(/&nbsp;/g, ' '))))
                    }
                  })
                  if (frag.lastChild.nodeName != 'BR') {
                    frag.appendChild(me.document.createElement('br'))
                  }
                }
              }
            } else {
              frag.appendChild(me.document.createTextNode(utils.html(node.data.replace(/&nbsp;/g, ' '))))
            }
            if (!node.nextSibling() && frag.lastChild.nodeName == 'BR') {
              frag.removeChild(frag.lastChild)
            }
          })
          rng.insertNode(frag).select()
        }
        return true
      }
    })
    me.addListener('keydown', function(cmd, evt) {
      var me = this
      var keyCode = evt.keyCode || evt.which
      if (keyCode == 40) {
        var rng = me.selection.getRange()
        var pre
        var start = rng.startContainer
        if (
          rng.collapsed &&
          (pre = domUtils.findParentByTagName(rng.startContainer, 'pre', true)) &&
          !pre.nextSibling
        ) {
          var last = pre.lastChild
          while (last && last.nodeName == 'BR') {
            last = last.previousSibling
          }
          if (last === start || (rng.startContainer === pre && rng.startOffset == pre.childNodes.length)) {
            me.execCommand('insertparagraph')
            domUtils.preventDefault(evt)
          }
        }
      }
    })
    me.addListener('delkeydown', function(type, evt) {
      var rng = this.selection.getRange()
      rng.txtToElmBoundary(true)
      var start = rng.startContainer
      if (domUtils.isTagNode(start, 'pre') && rng.collapsed && domUtils.isStartInblock(rng)) {
        var p = me.document.createElement('p')
        domUtils.fillNode(me.document, p)
        start.parentNode.insertBefore(p, start)
        domUtils.remove(start)
        rng.setStart(p, 0).setCursor(false, true)
        domUtils.preventDefault(evt)
        return true
      }
    })
  }
  UE.commands['cleardoc'] = {
    execCommand: function(cmdName) {
      var me = this
      var enterTag = me.options.enterTag
      var range = me.selection.getRange()
      if (enterTag == 'br') {
        me.body.innerHTML = '<br/>'
        range.setStart(me.body, 0).setCursor()
      } else {
        me.body.innerHTML = '<p style="font-size:18px">' + (ie ? '' : '<br/>') + '</p>'
        range.setStart(me.body.firstChild, 0).setCursor(false, true)
      }
      setTimeout(function() {
        me.fireEvent('clearDoc')
      }, 0)
    }
  }
  UE.commands['fullangledetection'] = {
    execCommand: function(cmdName) {
      var me = this
      var enterTag = me.options.enterTag
      var range = me.selection.getRange()
      let copyHtmlBody = me.body.innerHTML
      // range.setStart(me.body.firstChild, 0).setCursor(false, true)
      const fullangles = Array.from(new Set(copyHtmlBody.match(/[\uff00-\uffff]/g)))
      if (fullangles.length > 0) {
        console.log('fullangle', fullangles)
        if (confirm('全角符号将渲染红色')) {
          // 先恢复
          for (i = 0; i < fullangles.length; i++) {
            copyHtmlBody = copyHtmlBody.replaceAll('<span style="color:white;background: indianred">' + fullangles[i] + '</span>', fullangles[i])
          }
          // 再赋值
          for (i = 0; i < fullangles.length; i++) {
            copyHtmlBody = copyHtmlBody.replaceAll(fullangles[i], '<span style="color:white;background: indianred">' + fullangles[i] + '</span>')
          }
          // 优化去除
          // copyHtmlBody = copyHtmlBody.replaceAll('</span><span style="color:indianred">', '')
          console.log('fullangle', copyHtmlBody)
          me.body.innerHTML = copyHtmlBody
          range.setStart(me.body.firstChild, 0).setCursor(false, true)
        }
      }

      setTimeout(function() {
        me.fireEvent('fullangledetection')
      }, 0)
    }
  }
  UE.commands['formatstandard'] = {
    execCommand: function(cmdName, tags, style, attrs, notIncludeA) {
      if (!confirm('一键标准格式化')) {
        return
      }
      let me = this
      let options = {
        removeFormatTags: 'b,big,code,del,dfn,em,font,i,ins,kbd,q,samp,small,span,strike,strong,sub,sup,tt,u,var',
        removeFormatAttributes: 'class,style'
      }
      var tagReg = new RegExp('^(?:' + (tags || options.removeFormatTags).replace(/,/g, '|') + ')$', 'i')
      var removeFormatAttributes = style ? [] : (attrs || options.removeFormatAttributes).split(',')
      var range = new dom.Range(this.document)
      var bookmark
      var node
      var parent
      var filter = function(node) {
        return node.nodeType == 1
      }

      function isRedundantSpan(node) {
        if (node.nodeType == 3 || node.tagName.toLowerCase() != 'span') {
          return 0
        }
        if (browser.ie) {
          var attrs = node.attributes
          if (attrs.length) {
            for (var i = 0, l = attrs.length; i < l; i++) {
              if (attrs[i].specified) {
                return 0
              }
            }
            return 1
          }
        }
        return !node.attributes.length
      }

      function doRemove(range) {
        var bookmark1 = range.createBookmark()
        if (range.collapsed) {
          range.enlarge(true)
        }
        if (!notIncludeA) {
          var aNode = domUtils.findParentByTagName(range.startContainer, 'a', true)
          if (aNode) {
            range.setStartBefore(aNode)
          }
          aNode = domUtils.findParentByTagName(range.endContainer, 'a', true)
          if (aNode) {
            range.setEndAfter(aNode)
          }
        }
        bookmark = range.createBookmark()
        node = bookmark.start
        while ((parent = node.parentNode) && !domUtils.isBlockElm(parent)) {
          domUtils.breakParent(node, parent)
          domUtils.clearEmptySibling(node)
        }
        if (bookmark.end) {
          node = bookmark.end
          while ((parent = node.parentNode) && !domUtils.isBlockElm(parent)) {
            domUtils.breakParent(node, parent)
            domUtils.clearEmptySibling(node)
          }
          var current = domUtils.getNextDomNode(bookmark.start, false, filter)
          var next
          while (current) {
            if (current == bookmark.end) {
              break
            }
            next = domUtils.getNextDomNode(current, true, filter)
            if (!dtd.$empty[current.tagName.toLowerCase()] && !domUtils.isBookmarkNode(current)) {
              if (tagReg.test(current.tagName)) {
                if (style) {
                  domUtils.removeStyle(current, style)
                  if (isRedundantSpan(current) && style != 'text-decoration') {
                    domUtils.remove(current, true)
                  }
                } else {
                  domUtils.remove(current, true)
                }
              } else {
                if (!dtd.$tableContent[current.tagName] && !dtd.$list[current.tagName]) {
                  domUtils.standardAttributes(current, removeFormatAttributes)
                  if (isRedundantSpan(current)) {
                    domUtils.remove(current, true)
                  }
                }
              }
            }
            current = next
          }
        }
        var pN = bookmark.start.parentNode
        if (domUtils.isBlockElm(pN) && !dtd.$tableContent[pN.tagName] && !dtd.$list[pN.tagName]) {
          domUtils.standardAttributes(pN, removeFormatAttributes)
        }
        pN = bookmark.end.parentNode
        if (bookmark.end && domUtils.isBlockElm(pN) && !dtd.$tableContent[pN.tagName] && !dtd.$list[pN.tagName]) {
          domUtils.standardAttributes(pN, removeFormatAttributes)
        }
        range.moveToBookmark(bookmark).moveToBookmark(bookmark1)
        var node = range.startContainer
        var tmp
        var collapsed = range.collapsed
        while (node.nodeType == 1 && domUtils.isEmptyNode(node) && dtd.$removeEmpty[node.tagName]) {
          tmp = node.parentNode
          range.setStartBefore(node)
          if (range.startContainer === range.endContainer) {
            range.endOffset--
          }
          domUtils.remove(node)
          node = tmp
        }
        if (!collapsed) {
          node = range.endContainer
          while (node.nodeType == 1 && domUtils.isEmptyNode(node) && dtd.$removeEmpty[node.tagName]) {
            tmp = node.parentNode
            range.setEndBefore(node)
            domUtils.remove(node)
            node = tmp
          }
        }
      }

      range = this.selection.getRange()
      doRemove(range)
      range.select()
      // let copyHtmlBody = me.body.innerHTML
      // // range.setStart(me.body.firstChild, 0).setCursor(false, true)
      // if (confirm('一键标准格式化')) {
      //   // 先恢复
      //   for (i = 0; i < fullangles.length; i++) {
      //     copyHtmlBody = copyHtmlBody.replaceAll('<span style="color:white;background: indianred">' + fullangles[i] + '</span>', fullangles[i])
      //   }
      //   // 再赋值
      //   for (i = 0; i < fullangles.length; i++) {
      //     copyHtmlBody = copyHtmlBody.replaceAll(fullangles[i], '<span style="color:white;background: indianred">' + fullangles[i] + '</span>')
      //   }
      //   // 优化去除
      //   // copyHtmlBody = copyHtmlBody.replaceAll('</span><span style="color:indianred">', '')
      //   console.log('fullangle', copyHtmlBody)
      //   me.body.innerHTML = copyHtmlBody
      //   range.setStart(me.body.firstChild, 0).setCursor(false, true)
      // }

      setTimeout(function() {
        me.fireEvent('formatstandard')
      }, 5)
    }
  }
  UE.plugin.register('anchor', function() {
    return {
      bindEvents: {
        ready: function() {
          utils.cssRule(
            'anchor',
            '.anchorclass{background: url(\'' +
            this.options.themePath +
            this.options.theme +
            '/images/anchor.gif\') no-repeat scroll left center transparent;cursor: auto;display: inline-block;height: 16px;width: 15px;}',
            this.document
          )
        }
      },
      outputRule: function(root) {
        utils.each(root.getNodesByTagName('img'), function(a) {
          var val
          if ((val = a.getAttr('anchorname'))) {
            a.tagName = 'a'
            a.setAttr({ anchorname: '', name: val, class: '' })
          }
        })
      },
      inputRule: function(root) {
        utils.each(root.getNodesByTagName('a'), function(a) {
          var val
          if ((val = a.getAttr('name')) && !a.getAttr('href')) {
            a.tagName = 'img'
            a.setAttr({ anchorname: a.getAttr('name'), class: 'anchorclass' })
            a.setAttr('name')
          }
        })
      },
      commands: {
        anchor: {
          execCommand: function(cmd, name) {
            var range = this.selection.getRange()
            var img = range.getClosedNode()
            if (img && img.getAttribute('anchorname')) {
              if (name) {
                img.setAttribute('anchorname', name)
              } else {
                range.setStartBefore(img).setCursor()
                domUtils.remove(img)
              }
            } else {
              if (name) {
                var anchor = this.document.createElement('img')
                range.collapse(true)
                domUtils.setAttributes(anchor, { anchorname: name, class: 'anchorclass' })
                range
                  .insertNode(anchor)
                  .setStartAfter(anchor)
                  .setCursor(false, true)
              }
            }
          }
        }
      }
    }
  })
  UE.plugins['wordcount'] = function() {
    var me = this
    me.setOpt('wordCount', true)
    me.addListener('contentchange', function() {
      me.fireEvent('wordcount')
    })
    var timer
    me.addListener('ready', function() {
      var me = this
      domUtils.on(me.body, 'keyup', function(evt) {
        var code = evt.keyCode || evt.which
        var ignores = { '16': 1, '18': 1, '20': 1, '37': 1, '38': 1, '39': 1, '40': 1 }
        if (code in ignores) {
          return
        }
        clearTimeout(timer)
        timer = setTimeout(function() {
          me.fireEvent('wordcount')
        }, 200)
      })
    })
  }
  UE.plugins['pagebreak'] = function() {
    var me = this
    var notBreakTags = ['td']
    me.setOpt('pageBreakTag', '_ueditor_page_break_tag_')

    function fillNode(node) {
      if (domUtils.isEmptyBlock(node)) {
        var firstChild = node.firstChild
        var tmpNode
        while (firstChild && firstChild.nodeType == 1 && domUtils.isEmptyBlock(firstChild)) {
          tmpNode = firstChild
          firstChild = firstChild.firstChild
        }
        !tmpNode && (tmpNode = node)
        domUtils.fillNode(me.document, tmpNode)
      }
    }

    me.ready(function() {
      utils.cssRule(
        'pagebreak',
        '.pagebreak{display:block;clear:both !important;cursor:default !important;width: 100% !important;margin:0;}',
        me.document
      )
    })

    function isHr(node) {
      return node && node.nodeType == 1 && node.tagName == 'HR' && node.className == 'pagebreak'
    }

    me.addInputRule(function(root) {
      root.traversal(function(node) {
        if (node.type == 'text' && node.data == me.options.pageBreakTag) {
          var hr = UE.uNode.createElement(
            '<hr class="pagebreak" noshade="noshade" size="5" style="-webkit-user-select: none;">'
          )
          node.parentNode.insertBefore(hr, node)
          node.parentNode.removeChild(node)
        }
      })
    })
    me.addOutputRule(function(node) {
      utils.each(node.getNodesByTagName('hr'), function(n) {
        if (n.getAttr('class') == 'pagebreak') {
          var txt = UE.uNode.createText(me.options.pageBreakTag)
          n.parentNode.insertBefore(txt, n)
          n.parentNode.removeChild(n)
        }
      })
    })
    me.commands['pagebreak'] = {
      execCommand: function() {
        var range = me.selection.getRange()
        var hr = me.document.createElement('hr')
        domUtils.setAttributes(hr, { class: 'pagebreak', noshade: 'noshade', size: '5' })
        domUtils.unSelectable(hr)
        var node = domUtils.findParentByTagName(range.startContainer, notBreakTags, true)
        var parents = []
        var pN
        if (node) {
          switch (node.tagName) {
            case 'TD':
              pN = node.parentNode
              if (!pN.previousSibling) {
                var table = domUtils.findParentByTagName(pN, 'table')
                table.parentNode.insertBefore(hr, table)
                parents = domUtils.findParents(hr, true)
              } else {
                pN.parentNode.insertBefore(hr, pN)
                parents = domUtils.findParents(hr)
              }
              pN = parents[1]
              if (hr !== pN) {
                domUtils.breakParent(hr, pN)
              }
              me.fireEvent('afteradjusttable', me.document)
          }
        } else {
          if (!range.collapsed) {
            range.deleteContents()
            var start = range.startContainer
            while (!domUtils.isBody(start) && domUtils.isBlockElm(start) && domUtils.isEmptyNode(start)) {
              range.setStartBefore(start).collapse(true)
              domUtils.remove(start)
              start = range.startContainer
            }
          }
          range.insertNode(hr)
          var pN = hr.parentNode
          var nextNode
          while (!domUtils.isBody(pN)) {
            domUtils.breakParent(hr, pN)
            nextNode = hr.nextSibling
            if (nextNode && domUtils.isEmptyBlock(nextNode)) {
              domUtils.remove(nextNode)
            }
            pN = hr.parentNode
          }
          nextNode = hr.nextSibling
          var pre = hr.previousSibling
          if (isHr(pre)) {
            domUtils.remove(pre)
          } else {
            pre && fillNode(pre)
          }
          if (!nextNode) {
            var p = me.document.createElement('p')
            hr.parentNode.appendChild(p)
            domUtils.fillNode(me.document, p)
            range.setStart(p, 0).collapse(true)
          } else {
            if (isHr(nextNode)) {
              domUtils.remove(nextNode)
            } else {
              fillNode(nextNode)
            }
            range.setEndAfter(hr).collapse(false)
          }
          range.select(true)
        }
      }
    }
  }
  UE.plugin.register('wordimage', function() {
    var me = this
    var images = []
    return {
      commands: {
        wordimage: {
          execCommand: function() {
            var images = domUtils.getElementsByTagName(me.body, 'img')
            var urlList = []
            for (var i = 0, ci; (ci = images[i++]);) {
              var url = ci.getAttribute('word_img')
              url && urlList.push(url)
            }
            return urlList
          },
          queryCommandState: function() {
            images = domUtils.getElementsByTagName(me.body, 'img')
            for (var i = 0, ci; (ci = images[i++]);) {
              if (ci.getAttribute('word_img')) {
                return 1
              }
            }
            return -1
          },
          notNeedUndo: true
        }
      },
      inputRule: function(root) {
        utils.each(root.getNodesByTagName('img'), function(img) {
          var attrs = img.attrs
          var flag = parseInt(attrs.width) < 128 || parseInt(attrs.height) < 43
          var opt = me.options
          var src = opt.UEDITOR_HOME_URL + 'themes/default/images/spacer.gif'
          if (attrs['src'] && /^(?:(file:\/+))/.test(attrs['src'])) {
            img.setAttr({
              width: attrs.width,
              height: attrs.height,
              alt: attrs.alt,
              word_img: attrs.src,
              src: src,
              style:
                'background:url(' +
                (flag
                  ? opt.themePath + opt.theme + '/images/word.gif'
                  : opt.langPath + opt.lang + '/images/localimage.png') +
                ') no-repeat center center;border:1px solid #ddd'
            })
          }
        })
      }
    }
  })
  UE.plugins['dragdrop'] = function() {
    var me = this
    me.ready(function() {
      domUtils.on(this.body, 'dragend', function() {
        var rng = me.selection.getRange()
        var node = rng.getClosedNode() || me.selection.getStart()
        if (node && node.tagName == 'IMG') {
          var pre = node.previousSibling
          var next
          while ((next = node.nextSibling)) {
            if (next.nodeType == 1 && next.tagName == 'SPAN' && !next.firstChild) {
              domUtils.remove(next)
            } else {
              break
            }
          }
          if (
            ((pre && pre.nodeType == 1 && !domUtils.isEmptyBlock(pre)) || !pre) &&
            (!next || (next && !domUtils.isEmptyBlock(next)))
          ) {
            if (pre && pre.tagName == 'P' && !domUtils.isEmptyBlock(pre)) {
              pre.appendChild(node)
              domUtils.moveChild(next, pre)
              domUtils.remove(next)
            } else {
              if (next && next.tagName == 'P' && !domUtils.isEmptyBlock(next)) {
                next.insertBefore(node, next.firstChild)
              }
            }
            if (pre && pre.tagName == 'P' && domUtils.isEmptyBlock(pre)) {
              domUtils.remove(pre)
            }
            if (next && next.tagName == 'P' && domUtils.isEmptyBlock(next)) {
              domUtils.remove(next)
            }
            rng.selectNode(node).select()
            me.fireEvent('saveScene')
          }
        }
      })
    })
    me.addListener('keyup', function(type, evt) {
      var keyCode = evt.keyCode || evt.which
      if (keyCode == 13) {
        var rng = me.selection.getRange()
        var node
        if ((node = domUtils.findParentByTagName(rng.startContainer, 'p', true))) {
          if (domUtils.getComputedStyle(node, 'text-align') == 'center') {
            domUtils.removeStyle(node, 'text-align')
          }
        }
      }
    })
  }
  UE.plugins['undo'] = function() {
    var saveSceneTimer
    var me = this
    var maxUndoCount = me.options.maxUndoCount || 20
    var maxInputCount = me.options.maxInputCount || 20
    var fillchar = new RegExp(domUtils.fillChar + '|</hr>', 'gi')
    var noNeedFillCharTags = { ol: 1, ul: 1, table: 1, tbody: 1, tr: 1, body: 1 }
    var orgState = me.options.autoClearEmptyNode

    function compareAddr(indexA, indexB) {
      if (indexA.length != indexB.length) {
        return 0
      }
      for (var i = 0, l = indexA.length; i < l; i++) {
        if (indexA[i] != indexB[i]) {
          return 0
        }
      }
      return 1
    }

    function compareRangeAddress(rngAddrA, rngAddrB) {
      if (rngAddrA.collapsed != rngAddrB.collapsed) {
        return 0
      }
      if (
        !compareAddr(rngAddrA.startAddress, rngAddrB.startAddress) ||
        !compareAddr(rngAddrA.endAddress, rngAddrB.endAddress)
      ) {
        return 0
      }
      return 1
    }

    function UndoManager() {
      this.list = []
      this.index = 0
      this.hasUndo = false
      this.hasRedo = false
      this.undo = function() {
        if (this.hasUndo) {
          if (!this.list[this.index - 1] && this.list.length == 1) {
            this.reset()
            return
          }
          while (this.list[this.index].content == this.list[this.index - 1].content) {
            this.index--
            if (this.index == 0) {
              return this.restore(0)
            }
          }
          this.restore(--this.index)
        }
      }
      this.redo = function() {
        if (this.hasRedo) {
          while (this.list[this.index].content == this.list[this.index + 1].content) {
            this.index++
            if (this.index == this.list.length - 1) {
              return this.restore(this.index)
            }
          }
          this.restore(++this.index)
        }
      }
      this.restore = function() {
        var me = this.editor
        var scene = this.list[this.index]
        var root = UE.htmlparser(scene.content.replace(fillchar, ''))
        me.options.autoClearEmptyNode = false
        me.filterInputRule(root)
        me.options.autoClearEmptyNode = orgState
        me.document.body.innerHTML = root.toHtml()
        me.fireEvent('afterscencerestore')
        if (browser.ie) {
          utils.each(domUtils.getElementsByTagName(me.document, 'td th caption p'), function(node) {
            if (domUtils.isEmptyNode(node)) {
              domUtils.fillNode(me.document, node)
            }
          })
        }
        try {
          var rng = new dom.Range(me.document).moveToAddress(scene.address)
          rng.select(noNeedFillCharTags[rng.startContainer.nodeName.toLowerCase()])
        } catch (e) {
        }
        this.update()
        this.clearKey()
        me.fireEvent('reset', true)
      }
      this.getScene = function() {
        var me = this.editor
        var rng = me.selection.getRange()
        var rngAddress = rng.createAddress(false, true)
        me.fireEvent('beforegetscene')
        var root = UE.htmlparser(me.body.innerHTML)
        me.options.autoClearEmptyNode = false
        me.filterOutputRule(root)
        me.options.autoClearEmptyNode = orgState
        var cont = root.toHtml()
        me.fireEvent('aftergetscene')
        return { address: rngAddress, content: cont }
      }
      this.save = function(notCompareRange, notSetCursor) {
        clearTimeout(saveSceneTimer)
        var currentScene = this.getScene(notSetCursor)
        var lastScene = this.list[this.index]
        if (lastScene && lastScene.content != currentScene.content) {
          me.trigger('contentchange')
        }
        if (
          lastScene &&
          lastScene.content == currentScene.content &&
          (notCompareRange ? 1 : compareRangeAddress(lastScene.address, currentScene.address))
        ) {
          return
        }
        this.list = this.list.slice(0, this.index + 1)
        this.list.push(currentScene)
        if (this.list.length > maxUndoCount) {
          this.list.shift()
        }
        this.index = this.list.length - 1
        this.clearKey()
        this.update()
      }
      this.update = function() {
        this.hasRedo = !!this.list[this.index + 1]
        this.hasUndo = !!this.list[this.index - 1]
      }
      this.reset = function() {
        this.list = []
        this.index = 0
        this.hasUndo = false
        this.hasRedo = false
        this.clearKey()
      }
      this.clearKey = function() {
        keycont = 0
        lastKeyCode = null
      }
    }

    me.undoManger = new UndoManager()
    me.undoManger.editor = me

    function saveScene() {
      this.undoManger.save()
    }

    me.addListener('saveScene', function() {
      var args = Array.prototype.splice.call(arguments, 1)
      this.undoManger.save.apply(this.undoManger, args)
    })
    me.addListener('reset', function(type, exclude) {
      if (!exclude) {
        this.undoManger.reset()
      }
    })
    me.commands['redo'] = me.commands['undo'] = {
      execCommand: function(cmdName) {
        this.undoManger[cmdName]()
      },
      queryCommandState: function(cmdName) {
        return this.undoManger['has' + (cmdName.toLowerCase() == 'undo' ? 'Undo' : 'Redo')] ? 0 : -1
      },
      notNeedUndo: 1
    }
    var keys = { 16: 1, 17: 1, 18: 1, 37: 1, 38: 1, 39: 1, 40: 1 }
    var keycont = 0
    var lastKeyCode
    var inputType = false
    me.addListener('ready', function() {
      domUtils.on(this.body, 'compositionstart', function() {
        inputType = true
      })
      domUtils.on(this.body, 'compositionend', function() {
        inputType = false
      })
    })
    me.addshortcutkey({ Undo: 'ctrl+90', Redo: 'ctrl+89' })
    var isCollapsed = true
    me.addListener('keydown', function(type, evt) {
      var me = this
      var keyCode = evt.keyCode || evt.which
      if (!keys[keyCode] && !evt.ctrlKey && !evt.metaKey && !evt.shiftKey && !evt.altKey) {
        if (inputType) {
          return
        }
        if (!me.selection.getRange().collapsed) {
          me.undoManger.save(false, true)
          isCollapsed = false
          return
        }
        if (me.undoManger.list.length == 0) {
          me.undoManger.save(true)
        }
        clearTimeout(saveSceneTimer)

        function save(cont) {
          cont.undoManger.save(false, true)
          cont.fireEvent('selectionchange')
        }

        saveSceneTimer = setTimeout(function() {
          if (inputType) {
            var interalTimer = setInterval(function() {
              if (!inputType) {
                save(me)
                clearInterval(interalTimer)
              }
            }, 300)
            return
          }
          save(me)
        }, 200)
        lastKeyCode = keyCode
        keycont++
        if (keycont >= maxInputCount) {
          save(me)
        }
      }
    })
    me.addListener('keyup', function(type, evt) {
      var keyCode = evt.keyCode || evt.which
      if (!keys[keyCode] && !evt.ctrlKey && !evt.metaKey && !evt.shiftKey && !evt.altKey) {
        if (inputType) {
          return
        }
        if (!isCollapsed) {
          this.undoManger.save(false, true)
          isCollapsed = true
        }
      }
    })
    me.stopCmdUndo = function() {
      me.__hasEnterExecCommand = true
    }
    me.startCmdUndo = function() {
      me.__hasEnterExecCommand = false
    }
  }

  UE.plugin.register('copy', function() {
    var me = this

    function initZeroClipboard() {
      ZeroClipboard.config({
        debug: false,
        swfPath: me.options.UEDITOR_HOME_URL + 'third-party/zeroclipboard/ZeroClipboard.swf'
      })
      var client = (me.zeroclipboard = new ZeroClipboard())
      client.on('copy', function(e) {
        var client = e.client
        var rng = me.selection.getRange()
        var div = document.createElement('div')
        div.appendChild(rng.cloneContents())
        client.setText(div.innerText || div.textContent)
        client.setHtml(div.innerHTML)
        rng.select()
      })
      client.on('mouseover mouseout', function(e) {
        var target = e.target
        if (e.type == 'mouseover') {
          domUtils.addClass(target, 'edui-state-hover')
        } else {
          if (e.type == 'mouseout') {
            domUtils.removeClasses(target, 'edui-state-hover')
          }
        }
      })
      client.on('wrongflash noflash', function() {
        ZeroClipboard.destroy()
      })
    }

    return {
      bindEvents: {
        ready: function() {
          if (!browser.ie) {
            if (window.ZeroClipboard) {
              initZeroClipboard()
            } else {
              utils.loadFile(
                document,
                {
                  src: me.options.UEDITOR_HOME_URL + 'third-party/zeroclipboard/ZeroClipboard.js',
                  tag: 'script',
                  type: 'text/javascript',
                  defer: 'defer'
                },
                function() {
                  initZeroClipboard()
                }
              )
            }
          }
        }
      },
      commands: {
        copy: {
          execCommand: function(cmd) {
            if (!me.document.execCommand('copy')) {
              alert(me.getLang('copymsg'))
            }
          }
        }
      }
    }
  })
  UE.plugins['cut_text'] = function() {
    var me = this

    function initZeroClipboard() {
      ZeroClipboard.config({
        debug: false,
        swfPath: me.options.UEDITOR_HOME_URL + 'third-party/zeroclipboard/ZeroClipboard.swf'
      })
      var client = (me.zeroclipboard = new ZeroClipboard())
      client.on('copy', function(e) {
        var client = e.client
        var rng = me.selection.getRange()
        var div = document.createElement('div')
        div.appendChild(rng.cloneContents())
        client.setText(div.innerText || div.textContent)
        client.setHtml(div.innerHTML)
        rng.select()
      })
      client.on('mouseover mouseout', function(e) {
        var target = e.target
        if (e.type == 'mouseover') {
          domUtils.addClass(target, 'edui-state-hover')
        } else {
          if (e.type == 'mouseout') {
            domUtils.removeClasses(target, 'edui-state-hover')
          }
        }
      })
      client.on('wrongflash noflash', function() {
        ZeroClipboard.destroy()
      })
    }

    UE.plugins['ready'] = function() {
      if (!browser.ie) {
        if (window.ZeroClipboard) {
          initZeroClipboard()
        } else {
          utils.loadFile(
            document,
            {
              src: me.options.UEDITOR_HOME_URL + 'third-party/zeroclipboard/ZeroClipboard.js',
              tag: 'script',
              type: 'text/javascript',
              defer: 'defer'
            },
            function() {
              initZeroClipboard()
            }
          )
        }
      }
    }
    me.commands['cut_text'] = {
      execCommand: function(cmd) {
        if (!me.document.execCommand('cut')) {
          alert(me.getLang('cut_text_msg'))
        }
      }
    }
  }
  UE.plugins['paste'] = function() {
    function getClipboardData(callback) {
      var doc = this.document
      if (doc.getElementById('baidu_pastebin')) {
        return
      }
      var range = this.selection.getRange()
      var bk = range.createBookmark()
      var pastebin = doc.createElement('div')
      pastebin.id = 'baidu_pastebin'
      browser.webkit && pastebin.appendChild(doc.createTextNode(domUtils.fillChar + domUtils.fillChar))
      doc.body.appendChild(pastebin)
      bk.start.style.display = ''
      pastebin.style.cssText =
        'position:absolute;width:1px;height:1px;overflow:hidden;left:-1000px;white-space:nowrap;top:' +
        domUtils.getXY(bk.start).y +
        'px'
      range.selectNodeContents(pastebin).select(true)
      setTimeout(function() {
        if (browser.webkit) {
          for (var i = 0, pastebins = doc.querySelectorAll('#baidu_pastebin'), pi; (pi = pastebins[i++]);) {
            if (domUtils.isEmptyNode(pi)) {
              domUtils.remove(pi)
            } else {
              pastebin = pi
              break
            }
          }
        }
        try {
          pastebin.parentNode.removeChild(pastebin)
        } catch (e) {
        }
        range.moveToBookmark(bk).select(true)
        callback(pastebin)
      }, 200)
    }

    var me = this
    me.setOpt({ retainOnlyLabelPasted: false })
    var txtContent, htmlContent, address

    function getPureHtml(html) {
      return html.replace(/<(\/?)([\w\-]+)([^>]*)>/gi, function(a, b, tagName, attrs) {
        tagName = tagName.toLowerCase()
        if ({ img: 1 }[tagName]) {
          return a
        }
        attrs = attrs.replace(/([\w\-]*?)\s*=\s*(("([^"]*)")|('([^']*)')|([^\s>]+))/gi, function(str, atr, val) {
          if ({ src: 1, href: 1, name: 1 }[atr.toLowerCase()]) {
            return atr + '=' + val + ' '
          }
          return ''
        })
        if ({ span: 1, div: 1 }[tagName]) {
          return ''
        } else {
          return '<' + b + tagName + ' ' + utils.trim(attrs) + '>'
        }
      })
    }

    function filter(div) {
      var html
      if (div.firstChild) {
        var nodes = domUtils.getElementsByTagName(div, 'span')
        for (var i = 0, ni; (ni = nodes[i++]);) {
          if (ni.id == '_baidu_cut_start' || ni.id == '_baidu_cut_end') {
            domUtils.remove(ni)
          }
        }
        if (browser.webkit) {
          var brs = div.querySelectorAll('div br')
          for (var i = 0, bi; (bi = brs[i++]);) {
            var pN = bi.parentNode
            if (pN.tagName == 'DIV' && pN.childNodes.length == 1) {
              pN.innerHTML = '<p style="font-size:18px"><br/></p>'
              domUtils.remove(pN)
            }
          }
          var divs = div.querySelectorAll('#baidu_pastebin')
          for (var i = 0, di; (di = divs[i++]);) {
            var tmpP = me.document.createElement('p')
            di.parentNode.insertBefore(tmpP, di)
            while (di.firstChild) {
              tmpP.appendChild(di.firstChild)
            }
            domUtils.remove(di)
          }
          var metas = div.querySelectorAll('meta')
          for (var i = 0, ci; (ci = metas[i++]);) {
            domUtils.remove(ci)
          }
          var brs = div.querySelectorAll('br')
          for (i = 0; (ci = brs[i++]);) {
            if (/^apple-/i.test(ci.className)) {
              domUtils.remove(ci)
            }
          }
        }
        if (browser.gecko) {
          var dirtyNodes = div.querySelectorAll('[_moz_dirty]')
          for (i = 0; (ci = dirtyNodes[i++]);) {
            ci.removeAttribute('_moz_dirty')
          }
        }
        if (!browser.ie) {
          var spans = div.querySelectorAll('span.Apple-style-span')
          for (var i = 0, ci; (ci = spans[i++]);) {
            domUtils.remove(ci, true)
          }
        }
        html = div.innerHTML
        html = UE.filterWord(html)
        var root = UE.htmlparser(html)
        if (me.options.filterRules) {
          UE.filterNode(root, me.options.filterRules)
        }
        me.filterInputRule(root)
        if (browser.webkit) {
          var br = root.lastChild()
          if (br && br.type == 'element' && br.tagName == 'br') {
            root.removeChild(br)
          }
          utils.each(me.body.querySelectorAll('div'), function(node) {
            if (domUtils.isEmptyBlock(node)) {
              domUtils.remove(node, true)
            }
          })
        }
        html = { html: root.toHtml() }
        me.fireEvent('beforepaste', html, root)
        if (!html.html) {
          return
        }
        root = UE.htmlparser(html.html, true)
        if (me.queryCommandState('pasteplain') === 1) {
          me.execCommand('insertHtml', UE.filterNode(root, me.options.filterTxtRules).toHtml(), true)
        } else {
          UE.filterNode(root, me.options.filterTxtRules)
          txtContent = root.toHtml()
          htmlContent = html.html
          address = me.selection.getRange().createAddress(true)
          me.execCommand(
            'insertHtml',
            me.getOpt('retainOnlyLabelPasted') === true ? getPureHtml(htmlContent) : htmlContent,
            true
          )
        }
        me.fireEvent('afterpaste', html)
      }
    }

    me.addListener('pasteTransfer', function(cmd, plainType) {
      if (address && txtContent && htmlContent && txtContent != htmlContent) {
        var range = me.selection.getRange()
        range.moveToAddress(address, true)
        if (!range.collapsed) {
          while (!domUtils.isBody(range.startContainer)) {
            var start = range.startContainer
            if (start.nodeType == 1) {
              start = start.childNodes[range.startOffset]
              if (!start) {
                range.setStartBefore(range.startContainer)
                continue
              }
              var pre = start.previousSibling
              if (pre && pre.nodeType == 3 && new RegExp('^[\n\r\t ' + domUtils.fillChar + ']*$').test(pre.nodeValue)) {
                range.setStartBefore(pre)
              }
            }
            if (range.startOffset == 0) {
              range.setStartBefore(range.startContainer)
            } else {
              break
            }
          }
          while (!domUtils.isBody(range.endContainer)) {
            var end = range.endContainer
            if (end.nodeType == 1) {
              end = end.childNodes[range.endOffset]
              if (!end) {
                range.setEndAfter(range.endContainer)
                continue
              }
              var next = end.nextSibling
              if (
                next &&
                next.nodeType == 3 &&
                new RegExp('^[\n\r\t' + domUtils.fillChar + ']*$').test(next.nodeValue)
              ) {
                range.setEndAfter(next)
              }
            }
            if (
              range.endOffset ==
              range.endContainer[range.endContainer.nodeType == 3 ? 'nodeValue' : 'childNodes'].length
            ) {
              range.setEndAfter(range.endContainer)
            } else {
              break
            }
          }
        }
        range.deleteContents()
        range.select(true)
        me.__hasEnterExecCommand = true
        var html = htmlContent
        if (plainType === 2) {
          html = getPureHtml(html)
        } else {
          if (plainType) {
            html = txtContent
          }
        }
        me.execCommand('inserthtml', html, true)
        me.__hasEnterExecCommand = false
        var rng = me.selection.getRange()
        while (
          !domUtils.isBody(rng.startContainer) &&
          !rng.startOffset &&
          rng.startContainer[rng.startContainer.nodeType == 3 ? 'nodeValue' : 'childNodes'].length
          ) {
          rng.setStartBefore(rng.startContainer)
        }
        var tmpAddress = rng.createAddress(true)
        address.endAddress = tmpAddress.startAddress
      }
    })
    me.addListener('ready', function() {
      domUtils.on(me.body, 'cut', function() {
        var range = me.selection.getRange()
        if (!range.collapsed && me.undoManger) {
          me.undoManger.save()
        }
      })
      domUtils.on(me.body, browser.ie || browser.opera ? 'keydown' : 'paste', function(e) {
        if ((browser.ie || browser.opera) && ((!e.ctrlKey && !e.metaKey) || e.keyCode != '86')) {
          return
        }
        getClipboardData.call(me, function(div) {
          filter(div)
        })
      })
    })

    function insertHtml(span) {
      var range = me.selection.getRange()
      var start = range.startContainer
      var tmpNode
      while (start) {
        if (domUtils.isBody(start)) {
          break
        }
        tmpNode = start
        start = start.parentNode
      }
      if (tmpNode) {
        tmpNode.appendChild(span)
        // domUtils.remove(span)
        // domUtils.fillNode(me.document, span)
        // range.setStart(span, 0).setCursor(false, true)
      }
    }

    me.commands['paste'] = {
      execCommand: function(cmd) {
        if (me.document.execCommand('paste')) {
          getClipboardData.call(me, function(div) {
            filter(div)
          })
        } else {
          navigator.clipboard.read().then(data => {
            data.forEach(clipboardItem => {
              clipboardItem.types.forEach(type => {
                clipboardItem.getType(type).then(async data => {
                  console.log('遍历当前项数据', data)
                  if (type == 'text/plain') {
                    // 文本
                    // let spanEl = document.createElement('span');
                    // spanEl.innerText = await data.text();
                    // console.log('text',spanEl)
                    // insertHtml(spanEl)
                  } else if (type == 'image/png') {
                    // let imgEl = document.createElement('img')
                    // imgEl.src = URL.createObjectURL(data)
                    // // me.document.body.appendChild(imgEl);
                    // insertHtml(imgEl)
                  } else if (type == 'text/html') {
                    let spanEl = document.createElement('span')
                    let innerHtml = await data.text()
                    spanEl.innerHTML = domUtils.filterImgHtml(innerHtml)
                    console.log('html', spanEl)
                    insertHtml(spanEl)
                  }
                  getClipboardData.call(me, function(div) {
                    filter(div)
                  })
                })
              })
            })
          }).catch(err => {
            console.error('无法读取剪贴板内容：', err)
          })
        }
      }
    }
  }
  UE.plugins['pasteplain'] = function() {
    var me = this
    me.setOpt({
      pasteplain: false,
      filterTxtRules: (function() {
        function transP(node) {
          node.tagName = 'p'
          node.setStyle()
        }

        function removeNode(node) {
          node.parentNode.removeChild(node, true)
        }

        return {
          '-': 'script style object iframe embed input select',
          p: { $: {} },
          br: { $: {} },
          div: function(node) {
            var tmpNode
            var p = UE.uNode.createElement('p')
            while ((tmpNode = node.firstChild())) {
              if (tmpNode.type == 'text' || !UE.dom.dtd.$block[tmpNode.tagName]) {
                p.appendChild(tmpNode)
              } else {
                if (p.firstChild()) {
                  node.parentNode.insertBefore(p, node)
                  p = UE.uNode.createElement('p')
                } else {
                  node.parentNode.insertBefore(tmpNode, node)
                }
              }
            }
            if (p.firstChild()) {
              node.parentNode.insertBefore(p, node)
            }
            node.parentNode.removeChild(node)
          },
          ol: removeNode,
          ul: removeNode,
          dl: removeNode,
          dt: removeNode,
          dd: removeNode,
          li: removeNode,
          caption: transP,
          th: transP,
          tr: transP,
          h1: transP,
          h2: transP,
          h3: transP,
          h4: transP,
          h5: transP,
          h6: transP,
          td: function(node) {
            var txt = !!node.innerText()
            if (txt) {
              node.parentNode.insertAfter(UE.uNode.createText(' &nbsp; &nbsp;'), node)
            }
            node.parentNode.removeChild(node, node.innerText())
          }
        }
      })()
    })
    var pasteplain = me.options.pasteplain
    me.commands['pasteplain'] = {
      queryCommandState: function() {
        return pasteplain ? 1 : 0
      },
      execCommand: function() {
        pasteplain = !pasteplain | 0
      },
      notNeedUndo: 1
    }
  }
  UE.plugins['list'] = function() {
    var me = this
    var notExchange = { TD: 1, PRE: 1, BLOCKQUOTE: 1 }
    var customStyle = {
      cn: 'cn-1-',
      cn1: 'cn-2-',
      cn2: 'cn-3-',
      num: 'num-1-',
      num1: 'num-2-',
      num2: 'num-3-',
      dash: 'dash',
      dot: 'dot'
    }
    me.setOpt({
      autoTransWordToList: false,
      insertorderedlist: {
        num: '',
        num1: '',
        num2: '',
        cn: '',
        cn1: '',
        cn2: '',
        decimal: '',
        'lower-alpha': '',
        'lower-roman': '',
        'upper-alpha': '',
        'upper-roman': ''
      },
      insertunorderedlist: { circle: '', disc: '', square: '', dash: '', dot: '' },
      listDefaultPaddingLeft: '30',
      listiconpath: 'http://bs.baidu.com/listicon/',
      maxListLevel: -1,
      disablePInList: false
    })

    function listToArray(list) {
      var arr = []
      for (var p in list) {
        arr.push(p)
      }
      return arr
    }

    var listStyle = { OL: listToArray(me.options.insertorderedlist), UL: listToArray(me.options.insertunorderedlist) }
    var liiconpath = me.options.listiconpath
    for (var s in customStyle) {
      if (!me.options.insertorderedlist.hasOwnProperty(s) && !me.options.insertunorderedlist.hasOwnProperty(s)) {
        delete customStyle[s]
      }
    }
    me.ready(function() {
      var customCss = []
      for (var p in customStyle) {
        if (p == 'dash' || p == 'dot') {
          customCss.push(
            'li.list-' + customStyle[p] + '{background-image:url(' + liiconpath + customStyle[p] + '.gif)}'
          )
          customCss.push(
            'ul.custom_' +
            p +
            '{list-style:none;}ul.custom_' +
            p +
            ' li{background-position:0 3px;background-repeat:no-repeat}'
          )
        } else {
          for (var i = 0; i < 99; i++) {
            customCss.push(
              'li.list-' +
              customStyle[p] +
              i +
              '{background-image:url(' +
              liiconpath +
              'list-' +
              customStyle[p] +
              i +
              '.gif)}'
            )
          }
          customCss.push(
            'ol.custom_' +
            p +
            '{list-style:none;}ol.custom_' +
            p +
            ' li{background-position:0 3px;background-repeat:no-repeat}'
          )
        }
        switch (p) {
          case 'cn':
            customCss.push('li.list-' + p + '-paddingleft-1{padding-left:25px}')
            customCss.push('li.list-' + p + '-paddingleft-2{padding-left:40px}')
            customCss.push('li.list-' + p + '-paddingleft-3{padding-left:55px}')
            break
          case 'cn1':
            customCss.push('li.list-' + p + '-paddingleft-1{padding-left:30px}')
            customCss.push('li.list-' + p + '-paddingleft-2{padding-left:40px}')
            customCss.push('li.list-' + p + '-paddingleft-3{padding-left:55px}')
            break
          case 'cn2':
            customCss.push('li.list-' + p + '-paddingleft-1{padding-left:40px}')
            customCss.push('li.list-' + p + '-paddingleft-2{padding-left:55px}')
            customCss.push('li.list-' + p + '-paddingleft-3{padding-left:68px}')
            break
          case 'num':
          case 'num1':
            customCss.push('li.list-' + p + '-paddingleft-1{padding-left:25px}')
            break
          case 'num2':
            customCss.push('li.list-' + p + '-paddingleft-1{padding-left:35px}')
            customCss.push('li.list-' + p + '-paddingleft-2{padding-left:40px}')
            break
          case 'dash':
            customCss.push('li.list-' + p + '-paddingleft{padding-left:35px}')
            break
          case 'dot':
            customCss.push('li.list-' + p + '-paddingleft{padding-left:20px}')
        }
      }
      customCss.push('.list-paddingleft-1{padding-left:0}')
      customCss.push('.list-paddingleft-2{padding-left:' + me.options.listDefaultPaddingLeft + 'px}')
      customCss.push('.list-paddingleft-3{padding-left:' + me.options.listDefaultPaddingLeft * 2 + 'px}')
      utils.cssRule(
        'list',
        'ol,ul{margin:0;pading:0;' + (browser.ie ? '' : 'width:95%') + '}li{clear:both;}' + customCss.join('\n'),
        me.document
      )
    })
    me.ready(function() {
      domUtils.on(me.body, 'cut', function() {
        setTimeout(function() {
          var rng = me.selection.getRange()
          var li
          if (!rng.collapsed) {
            if ((li = domUtils.findParentByTagName(rng.startContainer, 'li', true))) {
              if (!li.nextSibling && domUtils.isEmptyBlock(li)) {
                var pn = li.parentNode
                var node
                if ((node = pn.previousSibling)) {
                  domUtils.remove(pn)
                  rng.setStartAtLast(node).collapse(true)
                  rng.select(true)
                } else {
                  if ((node = pn.nextSibling)) {
                    domUtils.remove(pn)
                    rng.setStartAtFirst(node).collapse(true)
                    rng.select(true)
                  } else {
                    var tmpNode = me.document.createElement('p')
                    domUtils.fillNode(me.document, tmpNode)
                    pn.parentNode.insertBefore(tmpNode, pn)
                    domUtils.remove(pn)
                    rng.setStart(tmpNode, 0).collapse(true)
                    rng.select(true)
                  }
                }
              }
            }
          }
        })
      })
    })

    function getStyle(node) {
      var cls = node.className
      if (domUtils.hasClass(node, /custom_/)) {
        return cls.match(/custom_(\w+)/)[1]
      }
      return domUtils.getStyle(node, 'list-style-type')
    }

    me.addListener('beforepaste', function(type, html) {
      var me = this
      var rng = me.selection.getRange()
      var li
      html.html = domUtils.filterImgHtml(html.html.toString())
      var root = UE.htmlparser(html.html, true)
      if ((li = domUtils.findParentByTagName(rng.startContainer, 'li', true))) {
        var list = li.parentNode
        var tagName = list.tagName == 'OL' ? 'ul' : 'ol'
        utils.each(root.getNodesByTagName(tagName), function(n) {
          n.tagName = list.tagName
          n.setAttr()
          if (n.parentNode === root) {
            type = getStyle(list) || (list.tagName == 'OL' ? 'decimal' : 'disc')
          } else {
            var className = n.parentNode.getAttr('class')
            if (className && /custom_/.test(className)) {
              type = className.match(/custom_(\w+)/)[1]
            } else {
              type = n.parentNode.getStyle('list-style-type')
            }
            if (!type) {
              type = list.tagName == 'OL' ? 'decimal' : 'disc'
            }
          }
          var index = utils.indexOf(listStyle[list.tagName], type)
          if (n.parentNode !== root) {
            index = index + 1 == listStyle[list.tagName].length ? 0 : index + 1
          }
          var currentStyle = listStyle[list.tagName][index]
          if (customStyle[currentStyle]) {
            n.setAttr('class', 'custom_' + currentStyle)
          } else {
            n.setStyle('list-style-type', currentStyle)
          }
        })
      }
      html.html = root.toHtml()
    })
    me.getOpt('disablePInList') === true &&
    me.addOutputRule(function(root) {
      utils.each(root.getNodesByTagName('li'), function(li) {
        var newChildrens = []
        var index = 0
        utils.each(li.children, function(n) {
          if (n.tagName == 'p') {
            var tmpNode
            while ((tmpNode = n.children.pop())) {
              newChildrens.splice(index, 0, tmpNode)
              tmpNode.parentNode = li
              lastNode = tmpNode
            }
            tmpNode = newChildrens[newChildrens.length - 1]
            if (!tmpNode || tmpNode.type != 'element' || tmpNode.tagName != 'br') {
              var br = UE.uNode.createElement('br')
              br.parentNode = li
              newChildrens.push(br)
            }
            index = newChildrens.length
          }
        })
        if (newChildrens.length) {
          li.children = newChildrens
        }
      })
    })
    me.addInputRule(function(root) {
      utils.each(root.getNodesByTagName('li'), function(li) {
        var tmpP = UE.uNode.createElement('p')
        for (var i = 0, ci; (ci = li.children[i]);) {
          if (ci.type == 'text' || dtd.p[ci.tagName]) {
            tmpP.appendChild(ci)
          } else {
            if (tmpP.firstChild()) {
              li.insertBefore(tmpP, ci)
              tmpP = UE.uNode.createElement('p')
              i = i + 2
            } else {
              i++
            }
          }
        }
        if ((tmpP.firstChild() && !tmpP.parentNode) || !li.firstChild()) {
          li.appendChild(tmpP)
        }
        if (!tmpP.firstChild()) {
          tmpP.innerHTML(browser.ie ? '&nbsp;' : '<br/>')
        }
        var p = li.firstChild()
        var lastChild = p.lastChild()
        if (lastChild && lastChild.type == 'text' && /^\s*$/.test(lastChild.data)) {
          p.removeChild(lastChild)
        }
      })
      if (me.options.autoTransWordToList) {
        var orderlisttype = {
          num1: /^\d+\)/,
          decimal: /^\d+\./,
          'lower-alpha': /^[a-z]+\)/,
          'upper-alpha': /^[A-Z]+\./,
          cn: /^[\u4E00\u4E8C\u4E09\u56DB\u516d\u4e94\u4e03\u516b\u4e5d]+[\u3001]/,
          cn2: /^\([\u4E00\u4E8C\u4E09\u56DB\u516d\u4e94\u4e03\u516b\u4e5d]+\)/
        }
        var unorderlisttype = { square: 'n' }

        function checkListType(content, container) {
          var span = container.firstChild()
          if (
            span &&
            span.type == 'element' &&
            span.tagName == 'span' &&
            /Wingdings|Symbol/.test(span.getStyle('font-family'))
          ) {
            for (var p in unorderlisttype) {
              if (unorderlisttype[p] == span.data) {
                return p
              }
            }
            return 'disc'
          }
          for (var p in orderlisttype) {
            if (orderlisttype[p].test(content)) {
              return p
            }
          }
        }

        utils.each(root.getNodesByTagName('p'), function(node) {
          if (node.getAttr('class') != 'MsoListParagraph') {
            return
          }
          node.setStyle('margin', '')
          node.setStyle('margin-left', '')
          node.setAttr('class', '')

          function appendLi(list, p, type) {
            if (list.tagName == 'ol') {
              if (browser.ie) {
                var first = p.firstChild()
                if (first.type == 'element' && first.tagName == 'span' && orderlisttype[type].test(first.innerText())) {
                  p.removeChild(first)
                }
              } else {
                p.innerHTML(p.innerHTML().replace(orderlisttype[type], ''))
              }
            } else {
              p.removeChild(p.firstChild())
            }
            var li = UE.uNode.createElement('li')
            li.appendChild(p)
            list.appendChild(li)
          }

          var tmp = node
          var type
          var cacheNode = node
          if (node.parentNode.tagName != 'li' && (type = checkListType(node.innerText(), node))) {
            var list = UE.uNode.createElement(me.options.insertorderedlist.hasOwnProperty(type) ? 'ol' : 'ul')
            if (customStyle[type]) {
              list.setAttr('class', 'custom_' + type)
            } else {
              list.setStyle('list-style-type', type)
            }
            while (node && node.parentNode.tagName != 'li' && checkListType(node.innerText(), node)) {
              tmp = node.nextSibling()
              if (!tmp) {
                node.parentNode.insertBefore(list, node)
              }
              appendLi(list, node, type)
              node = tmp
            }
            if (!list.parentNode && node && node.parentNode) {
              node.parentNode.insertBefore(list, node)
            }
          }
          var span = cacheNode.firstChild()
          if (span && span.type == 'element' && span.tagName == 'span' && /^\s*(&nbsp;)+\s*$/.test(span.innerText())) {
            span.parentNode.removeChild(span)
          }
        })
      }
    })
    me.addListener('contentchange', function() {
      adjustListStyle(me.document)
    })

    function adjustListStyle(doc, ignore) {
      utils.each(domUtils.getElementsByTagName(doc, 'ol ul'), function(node) {
        if (!domUtils.inDoc(node, doc)) {
          return
        }
        var parent = node.parentNode
        if (parent.tagName == node.tagName) {
          var nodeStyleType = getStyle(node) || (node.tagName == 'OL' ? 'decimal' : 'disc')
          var parentStyleType = getStyle(parent) || (parent.tagName == 'OL' ? 'decimal' : 'disc')
          if (nodeStyleType == parentStyleType) {
            var styleIndex = utils.indexOf(listStyle[node.tagName], nodeStyleType)
            styleIndex = styleIndex + 1 == listStyle[node.tagName].length ? 0 : styleIndex + 1
            setListStyle(node, listStyle[node.tagName][styleIndex])
          }
        }
        var index = 0
        var type = 2
        if (domUtils.hasClass(node, /custom_/)) {
          if (!(/[ou]l/i.test(parent.tagName) && domUtils.hasClass(parent, /custom_/))) {
            type = 1
          }
        } else {
          if (/[ou]l/i.test(parent.tagName) && domUtils.hasClass(parent, /custom_/)) {
            type = 3
          }
        }
        var style = domUtils.getStyle(node, 'list-style-type')
        style && (node.style.cssText = 'list-style-type:' + style)
        node.className = utils.trim(node.className.replace(/list-paddingleft-\w+/, '')) + ' list-paddingleft-' + type
        utils.each(domUtils.getElementsByTagName(node, 'li'), function(li) {
          li.style.cssText && (li.style.cssText = '')
          if (!li.firstChild) {
            domUtils.remove(li)
            return
          }
          if (li.parentNode !== node) {
            return
          }
          index++
          if (domUtils.hasClass(node, /custom_/)) {
            var paddingLeft = 1
            var currentStyle = getStyle(node)
            if (node.tagName == 'OL') {
              if (currentStyle) {
                switch (currentStyle) {
                  case 'cn':
                  case 'cn1':
                  case 'cn2':
                    if (index > 10 && (index % 10 == 0 || (index > 10 && index < 20))) {
                      paddingLeft = 2
                    } else {
                      if (index > 20) {
                        paddingLeft = 3
                      }
                    }
                    break
                  case 'num2':
                    if (index > 9) {
                      paddingLeft = 2
                    }
                }
              }
              li.className =
                'list-' +
                customStyle[currentStyle] +
                index +
                ' ' +
                'list-' +
                currentStyle +
                '-paddingleft-' +
                paddingLeft
            } else {
              li.className = 'list-' + customStyle[currentStyle] + ' ' + 'list-' + currentStyle + '-paddingleft'
            }
          } else {
            li.className = li.className.replace(/list-[\w\-]+/gi, '')
          }
          var className = li.getAttribute('class')
          if (className !== null && !className.replace(/\s/g, '')) {
            domUtils.removeAttributes(li, 'class')
          }
        })
        !ignore &&
        adjustList(
          node,
          node.tagName.toLowerCase(),
          getStyle(node) || domUtils.getStyle(node, 'list-style-type'),
          true
        )
      })
    }

    function adjustList(list, tag, style, ignoreEmpty) {
      var nextList = list.nextSibling
      if (
        nextList &&
        nextList.nodeType == 1 &&
        nextList.tagName.toLowerCase() == tag &&
        (getStyle(nextList) || domUtils.getStyle(nextList, 'list-style-type') || (tag == 'ol' ? 'decimal' : 'disc')) ==
        style
      ) {
        domUtils.moveChild(nextList, list)
        if (nextList.childNodes.length == 0) {
          domUtils.remove(nextList)
        }
      }
      if (nextList && domUtils.isFillChar(nextList)) {
        domUtils.remove(nextList)
      }
      var preList = list.previousSibling
      if (
        preList &&
        preList.nodeType == 1 &&
        preList.tagName.toLowerCase() == tag &&
        (getStyle(preList) || domUtils.getStyle(preList, 'list-style-type') || (tag == 'ol' ? 'decimal' : 'disc')) ==
        style
      ) {
        domUtils.moveChild(list, preList)
      }
      if (preList && domUtils.isFillChar(preList)) {
        domUtils.remove(preList)
      }
      !ignoreEmpty && domUtils.isEmptyBlock(list) && domUtils.remove(list)
      if (getStyle(list)) {
        adjustListStyle(list.ownerDocument, true)
      }
    }

    function setListStyle(list, style) {
      if (customStyle[style]) {
        list.className = 'custom_' + style
      }
      try {
        domUtils.setStyle(list, 'list-style-type', style)
      } catch (e) {
      }
    }

    function clearEmptySibling(node) {
      var tmpNode = node.previousSibling
      if (tmpNode && domUtils.isEmptyBlock(tmpNode)) {
        domUtils.remove(tmpNode)
      }
      tmpNode = node.nextSibling
      if (tmpNode && domUtils.isEmptyBlock(tmpNode)) {
        domUtils.remove(tmpNode)
      }
    }

    me.addListener('keydown', function(type, evt) {
      function preventAndSave() {
        evt.preventDefault ? evt.preventDefault() : (evt.returnValue = false)
        me.fireEvent('contentchange')
        me.undoManger && me.undoManger.save()
      }

      function findList(node, filterFn) {
        while (node && !domUtils.isBody(node)) {
          if (filterFn(node)) {
            return null
          }
          if (node.nodeType == 1 && /[ou]l/i.test(node.tagName)) {
            return node
          }
          node = node.parentNode
        }
        return null
      }

      var keyCode = evt.keyCode || evt.which
      if (keyCode == 13 && !evt.shiftKey) {
        var rng = me.selection.getRange()
        var parent = domUtils.findParent(
          rng.startContainer,
          function(node) {
            return domUtils.isBlockElm(node)
          },
          true
        )
        var li = domUtils.findParentByTagName(rng.startContainer, 'li', true)
        if (parent && parent.tagName != 'PRE' && !li) {
          var html = parent.innerHTML.replace(new RegExp(domUtils.fillChar, 'g'), '')
          if (/^\s*1\s*\.[^\d]/.test(html)) {
            parent.innerHTML = html.replace(/^\s*1\s*\./, '')
            rng
              .setStartAtLast(parent)
              .collapse(true)
              .select()
            me.__hasEnterExecCommand = true
            me.execCommand('insertorderedlist')
            me.__hasEnterExecCommand = false
          }
        }
        var range = me.selection.getRange()
        var start = findList(range.startContainer, function(node) {
          return node.tagName == 'TABLE'
        })
        var end = range.collapsed
          ? start
          : findList(range.endContainer, function(node) {
            return node.tagName == 'TABLE'
          })
        if (start && end && start === end) {
          if (!range.collapsed) {
            start = domUtils.findParentByTagName(range.startContainer, 'li', true)
            end = domUtils.findParentByTagName(range.endContainer, 'li', true)
            if (start && end && start === end) {
              range.deleteContents()
              li = domUtils.findParentByTagName(range.startContainer, 'li', true)
              if (li && domUtils.isEmptyBlock(li)) {
                pre = li.previousSibling
                next = li.nextSibling
                p = me.document.createElement('p')
                domUtils.fillNode(me.document, p)
                parentList = li.parentNode
                if (pre && next) {
                  range
                    .setStart(next, 0)
                    .collapse(true)
                    .select(true)
                  domUtils.remove(li)
                } else {
                  if ((!pre && !next) || !pre) {
                    parentList.parentNode.insertBefore(p, parentList)
                  } else {
                    li.parentNode.parentNode.insertBefore(p, parentList.nextSibling)
                  }
                  domUtils.remove(li)
                  if (!parentList.firstChild) {
                    domUtils.remove(parentList)
                  }
                  range.setStart(p, 0).setCursor()
                }
                preventAndSave()
                return
              }
            } else {
              var tmpRange = range.cloneRange()
              var bk = tmpRange.collapse(false).createBookmark()
              range.deleteContents()
              tmpRange.moveToBookmark(bk)
              var li = domUtils.findParentByTagName(tmpRange.startContainer, 'li', true)
              clearEmptySibling(li)
              tmpRange.select()
              preventAndSave()
              return
            }
          }
          li = domUtils.findParentByTagName(range.startContainer, 'li', true)
          if (li) {
            if (domUtils.isEmptyBlock(li)) {
              bk = range.createBookmark()
              var parentList = li.parentNode
              if (li !== parentList.lastChild) {
                domUtils.breakParent(li, parentList)
                clearEmptySibling(li)
              } else {
                parentList.parentNode.insertBefore(li, parentList.nextSibling)
                if (domUtils.isEmptyNode(parentList)) {
                  domUtils.remove(parentList)
                }
              }
              if (!dtd.$list[li.parentNode.tagName]) {
                if (!domUtils.isBlockElm(li.firstChild)) {
                  p = me.document.createElement('p')
                  li.parentNode.insertBefore(p, li)
                  while (li.firstChild) {
                    p.appendChild(li.firstChild)
                  }
                  domUtils.remove(li)
                } else {
                  domUtils.remove(li, true)
                }
              }
              range.moveToBookmark(bk).select()
            } else {
              var first = li.firstChild
              if (!first || !domUtils.isBlockElm(first)) {
                var p = me.document.createElement('p')
                !li.firstChild && domUtils.fillNode(me.document, p)
                while (li.firstChild) {
                  p.appendChild(li.firstChild)
                }
                li.appendChild(p)
                first = p
              }
              var span = me.document.createElement('span')
              range.insertNode(span)
              domUtils.breakParent(span, li)
              var nextLi = span.nextSibling
              first = nextLi.firstChild
              if (!first) {
                p = me.document.createElement('p')
                domUtils.fillNode(me.document, p)
                nextLi.appendChild(p)
                first = p
              }
              if (domUtils.isEmptyNode(first)) {
                first.innerHTML = ''
                domUtils.fillNode(me.document, first)
              }
              range
                .setStart(first, 0)
                .collapse(true)
                .shrinkBoundary()
                .select()
              domUtils.remove(span)
              var pre = nextLi.previousSibling
              if (pre && domUtils.isEmptyBlock(pre)) {
                pre.innerHTML = '<p style="font-size:18px"></p>'
                domUtils.fillNode(me.document, pre.firstChild)
              }
            }
            preventAndSave()
          }
        }
      }
      if (keyCode == 8) {
        range = me.selection.getRange()
        if (range.collapsed && domUtils.isStartInblock(range)) {
          tmpRange = range.cloneRange().trimBoundary()
          li = domUtils.findParentByTagName(range.startContainer, 'li', true)
          if (li && domUtils.isStartInblock(tmpRange)) {
            start = domUtils.findParentByTagName(range.startContainer, 'p', true)
            if (start && start !== li.firstChild) {
              var parentList = domUtils.findParentByTagName(start, ['ol', 'ul'])
              domUtils.breakParent(start, parentList)
              clearEmptySibling(start)
              me.fireEvent('contentchange')
              range.setStart(start, 0).setCursor(false, true)
              me.fireEvent('saveScene')
              domUtils.preventDefault(evt)
              return
            }
            if (li && (pre = li.previousSibling)) {
              if (keyCode == 46 && li.childNodes.length) {
                return
              }
              if (dtd.$list[pre.tagName]) {
                pre = pre.lastChild
              }
              me.undoManger && me.undoManger.save()
              first = li.firstChild
              if (domUtils.isBlockElm(first)) {
                if (domUtils.isEmptyNode(first)) {
                  pre.appendChild(first)
                  range.setStart(first, 0).setCursor(false, true)
                  while (li.firstChild) {
                    pre.appendChild(li.firstChild)
                  }
                } else {
                  span = me.document.createElement('span')
                  range.insertNode(span)
                  if (domUtils.isEmptyBlock(pre)) {
                    pre.innerHTML = ''
                  }
                  domUtils.moveChild(li, pre)
                  range
                    .setStartBefore(span)
                    .collapse(true)
                    .select(true)
                  domUtils.remove(span)
                }
              } else {
                if (domUtils.isEmptyNode(li)) {
                  var p = me.document.createElement('p')
                  pre.appendChild(p)
                  range.setStart(p, 0).setCursor()
                } else {
                  range
                    .setEnd(pre, pre.childNodes.length)
                    .collapse()
                    .select(true)
                  while (li.firstChild) {
                    pre.appendChild(li.firstChild)
                  }
                }
              }
              domUtils.remove(li)
              me.fireEvent('contentchange')
              me.fireEvent('saveScene')
              domUtils.preventDefault(evt)
              return
            }
            if (li && !li.previousSibling) {
              var parentList = li.parentNode
              var bk = range.createBookmark()
              if (domUtils.isTagNode(parentList.parentNode, 'ol ul')) {
                parentList.parentNode.insertBefore(li, parentList)
                if (domUtils.isEmptyNode(parentList)) {
                  domUtils.remove(parentList)
                }
              } else {
                while (li.firstChild) {
                  parentList.parentNode.insertBefore(li.firstChild, parentList)
                }
                domUtils.remove(li)
                if (domUtils.isEmptyNode(parentList)) {
                  domUtils.remove(parentList)
                }
              }
              range.moveToBookmark(bk).setCursor(false, true)
              me.fireEvent('contentchange')
              me.fireEvent('saveScene')
              domUtils.preventDefault(evt)
            }
          }
        }
      }
    })
    me.addListener('keyup', function(type, evt) {
      var keyCode = evt.keyCode || evt.which
      if (keyCode == 8) {
        var rng = me.selection.getRange()
        var list
        if ((list = domUtils.findParentByTagName(rng.startContainer, ['ol', 'ul'], true))) {
          adjustList(
            list,
            list.tagName.toLowerCase(),
            getStyle(list) || domUtils.getComputedStyle(list, 'list-style-type'),
            true
          )
        }
      }
    })
    me.addListener('tabkeydown', function() {
      var range = me.selection.getRange()

      function checkLevel(li) {
        if (me.options.maxListLevel != -1) {
          var level = li.parentNode
          var levelNum = 0
          while (/[ou]l/i.test(level.tagName)) {
            levelNum++
            level = level.parentNode
          }
          if (levelNum >= me.options.maxListLevel) {
            return true
          }
        }
      }

      var li = domUtils.findParentByTagName(range.startContainer, 'li', true)
      if (li) {
        var bk
        if (range.collapsed) {
          if (checkLevel(li)) {
            return true
          }
          var parentLi = li.parentNode
          var list = me.document.createElement(parentLi.tagName)
          var index = utils.indexOf(
            listStyle[list.tagName],
            getStyle(parentLi) || domUtils.getComputedStyle(parentLi, 'list-style-type')
          )
          index = index + 1 == listStyle[list.tagName].length ? 0 : index + 1
          var currentStyle = listStyle[list.tagName][index]
          setListStyle(list, currentStyle)
          if (domUtils.isStartInblock(range)) {
            me.fireEvent('saveScene')
            bk = range.createBookmark()
            parentLi.insertBefore(list, li)
            list.appendChild(li)
            adjustList(list, list.tagName.toLowerCase(), currentStyle)
            me.fireEvent('contentchange')
            range.moveToBookmark(bk).select(true)
            return true
          }
        } else {
          me.fireEvent('saveScene')
          bk = range.createBookmark()
          for (var i = 0, closeList, parents = domUtils.findParents(li), ci; (ci = parents[i++]);) {
            if (domUtils.isTagNode(ci, 'ol ul')) {
              closeList = ci
              break
            }
          }
          var current = li
          if (bk.end) {
            while (current && !(domUtils.getPosition(current, bk.end) & domUtils.POSITION_FOLLOWING)) {
              if (checkLevel(current)) {
                current = domUtils.getNextDomNode(current, false, null, function(node) {
                  return node !== closeList
                })
                continue
              }
              var parentLi = current.parentNode
              var list = me.document.createElement(parentLi.tagName)
              var index = utils.indexOf(
                listStyle[list.tagName],
                getStyle(parentLi) || domUtils.getComputedStyle(parentLi, 'list-style-type')
              )
              var currentIndex = index + 1 == listStyle[list.tagName].length ? 0 : index + 1
              var currentStyle = listStyle[list.tagName][currentIndex]
              setListStyle(list, currentStyle)
              parentLi.insertBefore(list, current)
              while (current && !(domUtils.getPosition(current, bk.end) & domUtils.POSITION_FOLLOWING)) {
                li = current.nextSibling
                list.appendChild(current)
                if (!li || domUtils.isTagNode(li, 'ol ul')) {
                  if (li) {
                    while ((li = li.firstChild)) {
                      if (li.tagName == 'LI') {
                        break
                      }
                    }
                  } else {
                    li = domUtils.getNextDomNode(current, false, null, function(node) {
                      return node !== closeList
                    })
                  }
                  break
                }
                current = li
              }
              adjustList(list, list.tagName.toLowerCase(), currentStyle)
              current = li
            }
          }
          me.fireEvent('contentchange')
          range.moveToBookmark(bk).select()
          return true
        }
      }
    })

    function getLi(start) {
      while (start && !domUtils.isBody(start)) {
        if (start.nodeName == 'TABLE') {
          return null
        }
        if (start.nodeName == 'LI') {
          return start
        }
        start = start.parentNode
      }
    }

    me.commands['insertorderedlist'] = me.commands['insertunorderedlist'] = {
      execCommand: function(command, style) {
        if (!style) {
          style = command.toLowerCase() == 'insertorderedlist' ? 'decimal' : 'disc'
        }
        var me = this
        var range = this.selection.getRange()
        var filterFn = function(node) {
          return node.nodeType == 1 ? node.tagName.toLowerCase() != 'br' : !domUtils.isWhitespace(node)
        }
        var tag = command.toLowerCase() == 'insertorderedlist' ? 'ol' : 'ul'
        var frag = me.document.createDocumentFragment()
        range.adjustmentBoundary().shrinkBoundary()
        var bko = range.createBookmark(true)
        var start = getLi(me.document.getElementById(bko.start))
        var modifyStart = 0
        var end = getLi(me.document.getElementById(bko.end))
        var modifyEnd = 0
        var startParent
        var endParent
        var list
        var tmp
        if (start || end) {
          start && (startParent = start.parentNode)
          if (!bko.end) {
            end = start
          }
          end && (endParent = end.parentNode)
          if (startParent === endParent) {
            while (start !== end) {
              tmp = start
              start = start.nextSibling
              if (!domUtils.isBlockElm(tmp.firstChild)) {
                var p = me.document.createElement('p')
                while (tmp.firstChild) {
                  p.appendChild(tmp.firstChild)
                }
                tmp.appendChild(p)
              }
              frag.appendChild(tmp)
            }
            tmp = me.document.createElement('span')
            startParent.insertBefore(tmp, end)
            if (!domUtils.isBlockElm(end.firstChild)) {
              p = me.document.createElement('p')
              while (end.firstChild) {
                p.appendChild(end.firstChild)
              }
              end.appendChild(p)
            }
            frag.appendChild(end)
            domUtils.breakParent(tmp, startParent)
            if (domUtils.isEmptyNode(tmp.previousSibling)) {
              domUtils.remove(tmp.previousSibling)
            }
            if (domUtils.isEmptyNode(tmp.nextSibling)) {
              domUtils.remove(tmp.nextSibling)
            }
            var nodeStyle =
              getStyle(startParent) ||
              domUtils.getComputedStyle(startParent, 'list-style-type') ||
              (command.toLowerCase() == 'insertorderedlist' ? 'decimal' : 'disc')
            if (startParent.tagName.toLowerCase() == tag && nodeStyle == style) {
              for (var i = 0, ci, tmpFrag = me.document.createDocumentFragment(); (ci = frag.firstChild);) {
                if (domUtils.isTagNode(ci, 'ol ul')) {
                  tmpFrag.appendChild(ci)
                } else {
                  while (ci.firstChild) {
                    tmpFrag.appendChild(ci.firstChild)
                    domUtils.remove(ci)
                  }
                }
              }
              tmp.parentNode.insertBefore(tmpFrag, tmp)
            } else {
              list = me.document.createElement(tag)
              setListStyle(list, style)
              list.appendChild(frag)
              tmp.parentNode.insertBefore(list, tmp)
            }
            domUtils.remove(tmp)
            list && adjustList(list, tag, style)
            range.moveToBookmark(bko).select()
            return
          }
          if (start) {
            while (start) {
              tmp = start.nextSibling
              if (domUtils.isTagNode(start, 'ol ul')) {
                frag.appendChild(start)
              } else {
                var tmpfrag = me.document.createDocumentFragment()
                var hasBlock = 0
                while (start.firstChild) {
                  if (domUtils.isBlockElm(start.firstChild)) {
                    hasBlock = 1
                  }
                  tmpfrag.appendChild(start.firstChild)
                }
                if (!hasBlock) {
                  var tmpP = me.document.createElement('p')
                  tmpP.appendChild(tmpfrag)
                  frag.appendChild(tmpP)
                } else {
                  frag.appendChild(tmpfrag)
                }
                domUtils.remove(start)
              }
              start = tmp
            }
            startParent.parentNode.insertBefore(frag, startParent.nextSibling)
            if (domUtils.isEmptyNode(startParent)) {
              range.setStartBefore(startParent)
              domUtils.remove(startParent)
            } else {
              range.setStartAfter(startParent)
            }
            modifyStart = 1
          }
          if (end && domUtils.inDoc(endParent, me.document)) {
            start = endParent.firstChild
            while (start && start !== end) {
              tmp = start.nextSibling
              if (domUtils.isTagNode(start, 'ol ul')) {
                frag.appendChild(start)
              } else {
                tmpfrag = me.document.createDocumentFragment()
                hasBlock = 0
                while (start.firstChild) {
                  if (domUtils.isBlockElm(start.firstChild)) {
                    hasBlock = 1
                  }
                  tmpfrag.appendChild(start.firstChild)
                }
                if (!hasBlock) {
                  tmpP = me.document.createElement('p')
                  tmpP.appendChild(tmpfrag)
                  frag.appendChild(tmpP)
                } else {
                  frag.appendChild(tmpfrag)
                }
                domUtils.remove(start)
              }
              start = tmp
            }
            var tmpDiv = domUtils.createElement(me.document, 'div', { tmpDiv: 1 })
            domUtils.moveChild(end, tmpDiv)
            frag.appendChild(tmpDiv)
            domUtils.remove(end)
            endParent.parentNode.insertBefore(frag, endParent)
            range.setEndBefore(endParent)
            if (domUtils.isEmptyNode(endParent)) {
              domUtils.remove(endParent)
            }
            modifyEnd = 1
          }
        }
        if (!modifyStart) {
          range.setStartBefore(me.document.getElementById(bko.start))
        }
        if (bko.end && !modifyEnd) {
          range.setEndAfter(me.document.getElementById(bko.end))
        }
        range.enlarge(true, function(node) {
          return notExchange[node.tagName]
        })
        frag = me.document.createDocumentFragment()
        var bk = range.createBookmark()
        var current = domUtils.getNextDomNode(bk.start, false, filterFn)
        var tmpRange = range.cloneRange()
        var tmpNode
        var block = domUtils.isBlockElm
        while (current && current !== bk.end && domUtils.getPosition(current, bk.end) & domUtils.POSITION_PRECEDING) {
          if (current.nodeType == 3 || dtd.li[current.tagName]) {
            if (current.nodeType == 1 && dtd.$list[current.tagName]) {
              while (current.firstChild) {
                frag.appendChild(current.firstChild)
              }
              tmpNode = domUtils.getNextDomNode(current, false, filterFn)
              domUtils.remove(current)
              current = tmpNode
              continue
            }
            tmpNode = current
            tmpRange.setStartBefore(current)
            while (current && current !== bk.end && (!block(current) || domUtils.isBookmarkNode(current))) {
              tmpNode = current
              current = domUtils.getNextDomNode(current, false, null, function(node) {
                return !notExchange[node.tagName]
              })
            }
            if (current && block(current)) {
              tmp = domUtils.getNextDomNode(tmpNode, false, filterFn)
              if (tmp && domUtils.isBookmarkNode(tmp)) {
                current = domUtils.getNextDomNode(tmp, false, filterFn)
                tmpNode = tmp
              }
            }
            tmpRange.setEndAfter(tmpNode)
            current = domUtils.getNextDomNode(tmpNode, false, filterFn)
            var li = range.document.createElement('li')
            li.appendChild(tmpRange.extractContents())
            if (domUtils.isEmptyNode(li)) {
              var tmpNode = range.document.createElement('p')
              while (li.firstChild) {
                tmpNode.appendChild(li.firstChild)
              }
              li.appendChild(tmpNode)
            }
            frag.appendChild(li)
          } else {
            current = domUtils.getNextDomNode(current, true, filterFn)
          }
        }
        range.moveToBookmark(bk).collapse(true)
        list = me.document.createElement(tag)
        setListStyle(list, style)
        list.appendChild(frag)
        range.insertNode(list)
        adjustList(list, tag, style)
        for (var i = 0, ci, tmpDivs = domUtils.getElementsByTagName(list, 'div'); (ci = tmpDivs[i++]);) {
          if (ci.getAttribute('tmpDiv')) {
            domUtils.remove(ci, true)
          }
        }
        range.moveToBookmark(bko).select()
      },
      queryCommandState: function(command) {
        var tag = command.toLowerCase() == 'insertorderedlist' ? 'ol' : 'ul'
        var path = this.selection.getStartElementPath()
        for (var i = 0, ci; (ci = path[i++]);) {
          if (ci.nodeName == 'TABLE') {
            return 0
          }
          if (tag == ci.nodeName.toLowerCase()) {
            return 1
          }
        }
        return 0
      },
      queryCommandValue: function(command) {
        var tag = command.toLowerCase() == 'insertorderedlist' ? 'ol' : 'ul'
        var path = this.selection.getStartElementPath()
        var node
        for (var i = 0, ci; (ci = path[i++]);) {
          if (ci.nodeName == 'TABLE') {
            node = null
            break
          }
          if (tag == ci.nodeName.toLowerCase()) {
            node = ci
            break
          }
        }
        return node ? getStyle(node) || domUtils.getComputedStyle(node, 'list-style-type') : null
      }
    }
  }
  ;(function() {
    var sourceEditors = {
      textarea: function(editor, holder) {
        var textarea = holder.ownerDocument.createElement('textarea')
        textarea.style.cssText =
          'position:absolute;resize:none;width:100%;height:100%;border:0;padding:0;margin:0;overflow-y:auto;'
        if (browser.ie && browser.version < 8) {
          textarea.style.width = holder.offsetWidth + 'px'
          textarea.style.height = holder.offsetHeight + 'px'
          holder.onresize = function() {
            textarea.style.width = holder.offsetWidth + 'px'
            textarea.style.height = holder.offsetHeight + 'px'
          }
        }
        holder.appendChild(textarea)
        return {
          setContent: function(content) {
            textarea.value = content
          },
          getContent: function() {
            return textarea.value
          },
          select: function() {
            var range
            if (browser.ie) {
              range = textarea.createTextRange()
              range.collapse(true)
              range.select()
            } else {
              textarea.setSelectionRange(0, 0)
              textarea.focus()
            }
          },
          dispose: function() {
            holder.removeChild(textarea)
            holder.onresize = null
            textarea = null
            holder = null
          }
        }
      },
      codemirror: function(editor, holder) {
        var codeEditor = window.CodeMirror(holder, {
          mode: 'text/html',
          tabMode: 'indent',
          lineNumbers: true,
          lineWrapping: true
        })
        var dom = codeEditor.getWrapperElement()
        dom.style.cssText =
          'position:absolute;left:0;top:0;width:100%;height:100%;font-family:consolas,"Courier new",monospace;font-size:13px;'
        codeEditor.getScrollerElement().style.cssText = 'position:absolute;left:0;top:0;width:100%;height:100%;'
        codeEditor.refresh()
        return {
          getCodeMirror: function() {
            return codeEditor
          },
          setContent: function(content) {
            codeEditor.setValue(content)
          },
          getContent: function() {
            return codeEditor.getValue()
          },
          select: function() {
            codeEditor.focus()
          },
          dispose: function() {
            holder.removeChild(dom)
            dom = null
            codeEditor = null
          }
        }
      }
    }
    UE.plugins['source'] = function() {
      var me = this
      var opt = this.options
      var sourceMode = false
      var sourceEditor
      var orgSetContent
      opt.sourceEditor = browser.ie ? 'textarea' : opt.sourceEditor || 'codemirror'
      me.setOpt({ sourceEditorFirst: false })

      function createSourceEditor(holder) {
        return sourceEditors[opt.sourceEditor == 'codemirror' && window.CodeMirror ? 'codemirror' : 'textarea'](
          me,
          holder
        )
      }

      var bakCssText
      var oldGetContent, bakAddress
      me.commands['source'] = {
        execCommand: function() {
          sourceMode = !sourceMode
          if (sourceMode) {
            bakAddress = me.selection.getRange().createAddress(false, true)
            me.undoManger && me.undoManger.save(true)
            if (browser.gecko) {
              me.body.contentEditable = false
            }
            bakCssText = me.iframe.style.cssText
            me.iframe.style.cssText += 'position:absolute;left:-32768px;top:-32768px;'
            me.fireEvent('beforegetcontent')
            var root = UE.htmlparser(me.body.innerHTML)
            me.filterOutputRule(root)
            root.traversal(function(node) {
              if (node.type == 'element') {
                switch (node.tagName) {
                  case 'td':
                  case 'th':
                  case 'caption':
                    if (node.children && node.children.length == 1) {
                      if (node.firstChild().tagName == 'br') {
                        node.removeChild(node.firstChild())
                      }
                    }
                    break
                  case 'pre':
                    node.innerText(node.innerText().replace(/&nbsp;/g, ' '))
                }
              }
            })
            me.fireEvent('aftergetcontent')
            var content = root.toHtml(true)
            sourceEditor = createSourceEditor(me.iframe.parentNode)
            sourceEditor.setContent(content)
            orgSetContent = me.setContent
            me.setContent = function(html) {
              var root = UE.htmlparser(html)
              me.filterInputRule(root)
              html = root.toHtml()
              sourceEditor.setContent(html)
            }
            setTimeout(function() {
              sourceEditor.select()
              me.addListener('fullscreenchanged', function() {
                try {
                  sourceEditor.getCodeMirror().refresh()
                } catch (e) {
                }
              })
            })
            oldGetContent = me.getContent
            me.getContent = function() {
              return sourceEditor.getContent() || '<p style="font-size:18px">' + (browser.ie ? '' : '<br/>') + '</p>'
            }
          } else {
            me.iframe.style.cssText = bakCssText
            var cont = sourceEditor.getContent() || '<p style="font-size:18px">' + (browser.ie ? '' : '<br/>') + '</p>'
            cont = cont.replace(new RegExp('[\\r\\t\\n ]*</?(\\w+)\\s*(?:[^>]*)>', 'g'), function(a, b) {
              if (b && !dtd.$inlineWithA[b.toLowerCase()]) {
                return a.replace(/(^[\n\r\t ]*)|([\n\r\t ]*$)/g, '')
              }
              return a.replace(/(^[\n\r\t]*)|([\n\r\t]*$)/g, '')
            })
            me.setContent = orgSetContent
            me.setContent(cont)
            sourceEditor.dispose()
            sourceEditor = null
            me.getContent = oldGetContent
            var first = me.body.firstChild
            if (!first) {
              me.body.innerHTML = '<p style="font-size:18px">' + (browser.ie ? '' : '<br/>') + '</p>'
              first = me.body.firstChild
            }
            me.undoManger && me.undoManger.save(true)
            if (browser.gecko) {
              var input = document.createElement('input')
              input.style.cssText = 'position:absolute;left:0;top:-32768px'
              document.body.appendChild(input)
              me.body.contentEditable = false
              setTimeout(function() {
                domUtils.setViewportOffset(input, { left: -32768, top: 0 })
                input.focus()
                setTimeout(function() {
                  me.body.contentEditable = true
                  me.selection
                    .getRange()
                    .moveToAddress(bakAddress)
                    .select(true)
                  domUtils.remove(input)
                })
              })
            } else {
              try {
                me.selection
                  .getRange()
                  .moveToAddress(bakAddress)
                  .select(true)
              } catch (e) {
              }
            }
          }
          this.fireEvent('sourcemodechanged', sourceMode)
        },
        queryCommandState: function() {
          return sourceMode | 0
        },
        notNeedUndo: 1
      }
      var oldQueryCommandState = me.queryCommandState
      me.queryCommandState = function(cmdName) {
        cmdName = cmdName.toLowerCase()
        if (sourceMode) {
          return cmdName in { source: 1, fullscreen: 1 } ? 1 : -1
        }
        return oldQueryCommandState.apply(this, arguments)
      }
      if (opt.sourceEditor == 'codemirror') {
        me.addListener('ready', function() {
          utils.loadFile(
            document,
            {
              src: opt.codeMirrorJsUrl || opt.UEDITOR_HOME_URL + 'third-party/codemirror/codemirror.js',
              tag: 'script',
              type: 'text/javascript',
              defer: 'defer'
            },
            function() {
              if (opt.sourceEditorFirst) {
                setTimeout(function() {
                  me.execCommand('source')
                }, 0)
              }
            }
          )
          utils.loadFile(document, {
            tag: 'link',
            rel: 'stylesheet',
            type: 'text/css',
            href: opt.codeMirrorCssUrl || opt.UEDITOR_HOME_URL + 'third-party/codemirror/codemirror.css'
          })
        })
      }
    }
  })()
  UE.plugins['enterkey'] = function() {
    var hTag
    var me = this
    var tag = me.options.enterTag
    me.addListener('keyup', function(type, evt) {
      var keyCode = evt.keyCode || evt.which
      if (keyCode == 13) {
        var range = me.selection.getRange()
        var start = range.startContainer
        var doSave
        if (!browser.ie) {
          if (/h\d/i.test(hTag)) {
            if (browser.gecko) {
              var h = domUtils.findParentByTagName(
                start,
                ['h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'blockquote', 'caption', 'table'],
                true
              )
              if (!h) {
                me.document.execCommand('formatBlock', false, '<p>')
                doSave = 1
              }
            } else {
              if (start.nodeType == 1) {
                var tmp = me.document.createTextNode('')
                var div
                range.insertNode(tmp)
                div = domUtils.findParentByTagName(tmp, 'div', true)
                if (div) {
                  var p = me.document.createElement('p')
                  while (div.firstChild) {
                    p.appendChild(div.firstChild)
                  }
                  div.parentNode.insertBefore(p, div)
                  domUtils.remove(div)
                  range.setStartBefore(tmp).setCursor()
                  doSave = 1
                }
                domUtils.remove(tmp)
              }
            }
            if (me.undoManger && doSave) {
              me.undoManger.save()
            }
          }
          browser.opera && range.select()
        } else {
          me.fireEvent('saveScene', true, true)
        }
      }
    })
    me.addListener('keydown', function(type, evt) {
      var keyCode = evt.keyCode || evt.which
      if (keyCode == 13) {
        if (me.fireEvent('beforeenterkeydown')) {
          domUtils.preventDefault(evt)
          return
        }
        me.fireEvent('saveScene', true, true)
        hTag = ''
        var range = me.selection.getRange()
        if (!range.collapsed) {
          var start = range.startContainer
          var end = range.endContainer
          var startTd = domUtils.findParentByTagName(start, 'td', true)
          var endTd = domUtils.findParentByTagName(end, 'td', true)
          if ((startTd && endTd && startTd !== endTd) || (!startTd && endTd) || (startTd && !endTd)) {
            evt.preventDefault ? evt.preventDefault() : (evt.returnValue = false)
            return
          }
        }
        if (tag == 'p') {
          if (!browser.ie) {
            start = domUtils.findParentByTagName(
              range.startContainer,
              ['ol', 'ul', 'p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'blockquote', 'caption'],
              true
            )
            if (!start && !browser.opera) {
              me.document.execCommand('formatBlock', false, '<p>')
              if (browser.gecko) {
                range = me.selection.getRange()
                start = domUtils.findParentByTagName(range.startContainer, 'p', true)
                start && domUtils.removeDirtyAttr(start)
              }
            } else {
              hTag = start.tagName
              start.tagName.toLowerCase() == 'p' && browser.gecko && domUtils.removeDirtyAttr(start)
            }
          }
        } else {
          evt.preventDefault ? evt.preventDefault() : (evt.returnValue = false)
          if (!range.collapsed) {
            range.deleteContents()
            start = range.startContainer
            if (start.nodeType == 1 && (start = start.childNodes[range.startOffset])) {
              while (start.nodeType == 1) {
                if (dtd.$empty[start.tagName]) {
                  range.setStartBefore(start).setCursor()
                  if (me.undoManger) {
                    me.undoManger.save()
                  }
                  return false
                }
                if (!start.firstChild) {
                  var br = range.document.createElement('br')
                  start.appendChild(br)
                  range.setStart(start, 0).setCursor()
                  if (me.undoManger) {
                    me.undoManger.save()
                  }
                  return false
                }
                start = start.firstChild
              }
              if (start === range.startContainer.childNodes[range.startOffset]) {
                br = range.document.createElement('br')
                range.insertNode(br).setCursor()
              } else {
                range.setStart(start, 0).setCursor()
              }
            } else {
              br = range.document.createElement('br')
              range
                .insertNode(br)
                .setStartAfter(br)
                .setCursor()
            }
          } else {
            br = range.document.createElement('br')
            range.insertNode(br)
            var parent = br.parentNode
            if (parent.lastChild === br) {
              br.parentNode.insertBefore(br.cloneNode(true), br)
              range.setStartBefore(br)
            } else {
              range.setStartAfter(br)
            }
            range.setCursor()
          }
        }
      }
    })
  }
  UE.plugins['keystrokes'] = function() {
    var me = this
    var collapsed = true
    me.addListener('keydown', function(type, evt) {
      var keyCode = evt.keyCode || evt.which
      var rng = me.selection.getRange()
      if (
        !rng.collapsed &&
        !(evt.ctrlKey || evt.shiftKey || evt.altKey || evt.metaKey) &&
        ((keyCode >= 65 && keyCode <= 90) ||
          (keyCode >= 48 && keyCode <= 57) ||
          (keyCode >= 96 && keyCode <= 111) ||
          { 13: 1, 8: 1, 46: 1 }[keyCode])
      ) {
        var tmpNode = rng.startContainer
        if (domUtils.isFillChar(tmpNode)) {
          rng.setStartBefore(tmpNode)
        }
        tmpNode = rng.endContainer
        if (domUtils.isFillChar(tmpNode)) {
          rng.setEndAfter(tmpNode)
        }
        rng.txtToElmBoundary()
        if (rng.endContainer && rng.endContainer.nodeType == 1) {
          tmpNode = rng.endContainer.childNodes[rng.endOffset]
          if (tmpNode && domUtils.isBr(tmpNode)) {
            rng.setEndAfter(tmpNode)
          }
        }
        if (rng.startOffset == 0) {
          tmpNode = rng.startContainer
          if (domUtils.isBoundaryNode(tmpNode, 'firstChild')) {
            tmpNode = rng.endContainer
            if (
              rng.endOffset == (tmpNode.nodeType == 3 ? tmpNode.nodeValue.length : tmpNode.childNodes.length) &&
              domUtils.isBoundaryNode(tmpNode, 'lastChild')
            ) {
              me.fireEvent('saveScene')
              me.body.innerHTML = '<p style="font-size:18px">' + (browser.ie ? '' : '<br/>') + '</p>'
              rng.setStart(me.body.firstChild, 0).setCursor(false, true)
              me._selectionChange()
              return
            }
          }
        }
      }
      if (keyCode == keymap.Backspace) {
        rng = me.selection.getRange()
        collapsed = rng.collapsed
        if (me.fireEvent('delkeydown', evt)) {
          return
        }
        var start, end
        if (rng.collapsed && rng.inFillChar()) {
          start = rng.startContainer
          if (domUtils.isFillChar(start)) {
            rng
              .setStartBefore(start)
              .shrinkBoundary(true)
              .collapse(true)
            domUtils.remove(start)
          } else {
            start.nodeValue = start.nodeValue.replace(new RegExp('^' + domUtils.fillChar), '')
            rng.startOffset--
            rng.collapse(true).select(true)
          }
        }
        if ((start = rng.getClosedNode())) {
          me.fireEvent('saveScene')
          rng.setStartBefore(start)
          domUtils.remove(start)
          rng.setCursor()
          me.fireEvent('saveScene')
          domUtils.preventDefault(evt)
          return
        }
        if (!browser.ie) {
          start = domUtils.findParentByTagName(rng.startContainer, 'table', true)
          end = domUtils.findParentByTagName(rng.endContainer, 'table', true)
          if ((start && !end) || (!start && end) || start !== end) {
            evt.preventDefault()
            return
          }
        }
      }
      if (keyCode == keymap.Tab) {
        var excludeTagNameForTabKey = { ol: 1, ul: 1, table: 1 }
        if (me.fireEvent('tabkeydown', evt)) {
          domUtils.preventDefault(evt)
          return
        }
        var range = me.selection.getRange()
        me.fireEvent('saveScene')
        for (
          var i = 0, txt = '', tabSize = me.options.tabSize || 4, tabNode = me.options.tabNode || '&nbsp;';
          i < tabSize;
          i++
        ) {
          txt += tabNode
        }
        var span = me.document.createElement('span')
        span.innerHTML = txt + domUtils.fillChar
        if (range.collapsed) {
          range.insertNode(span.cloneNode(true).firstChild).setCursor(true)
        } else {
          var filterFn = function(node) {
            return domUtils.isBlockElm(node) && !excludeTagNameForTabKey[node.tagName.toLowerCase()]
          }
          start = domUtils.findParent(range.startContainer, filterFn, true)
          end = domUtils.findParent(range.endContainer, filterFn, true)
          if (start && end && start === end) {
            range.deleteContents()
            range.insertNode(span.cloneNode(true).firstChild).setCursor(true)
          } else {
            var bookmark = range.createBookmark()
            range.enlarge(true)
            var bookmark2 = range.createBookmark()
            var current = domUtils.getNextDomNode(bookmark2.start, false, filterFn)
            while (current && !(domUtils.getPosition(current, bookmark2.end) & domUtils.POSITION_FOLLOWING)) {
              current.insertBefore(span.cloneNode(true).firstChild, current.firstChild)
              current = domUtils.getNextDomNode(current, false, filterFn)
            }
            range
              .moveToBookmark(bookmark2)
              .moveToBookmark(bookmark)
              .select()
          }
        }
        domUtils.preventDefault(evt)
      }
      if (browser.gecko && keyCode == 46) {
        range = me.selection.getRange()
        if (range.collapsed) {
          start = range.startContainer
          if (domUtils.isEmptyBlock(start)) {
            var parent = start.parentNode
            while (domUtils.getChildCount(parent) == 1 && !domUtils.isBody(parent)) {
              start = parent
              parent = parent.parentNode
            }
            if (start === parent.lastChild) {
              evt.preventDefault()
            }
          }
        }
      }
    })
    me.addListener('keyup', function(type, evt) {
      var keyCode = evt.keyCode || evt.which
      var rng
      var me = this
      if (keyCode == keymap.Backspace) {
        if (me.fireEvent('delkeyup')) {
          return
        }
        rng = me.selection.getRange()
        if (rng.collapsed) {
          var tmpNode
          var autoClearTagName = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6']
          if ((tmpNode = domUtils.findParentByTagName(rng.startContainer, autoClearTagName, true))) {
            if (domUtils.isEmptyBlock(tmpNode)) {
              var pre = tmpNode.previousSibling
              if (pre && pre.nodeName != 'TABLE') {
                domUtils.remove(tmpNode)
                rng.setStartAtLast(pre).setCursor(false, true)
                return
              } else {
                var next = tmpNode.nextSibling
                if (next && next.nodeName != 'TABLE') {
                  domUtils.remove(tmpNode)
                  rng.setStartAtFirst(next).setCursor(false, true)
                  return
                }
              }
            }
          }
          if (domUtils.isBody(rng.startContainer)) {
            var tmpNode = domUtils.createElement(me.document, 'p', {
              innerHTML: browser.ie ? domUtils.fillChar : '<br/>'
            })
            rng
              .insertNode(tmpNode)
              .setStart(tmpNode, 0)
              .setCursor(false, true)
          }
        }
        if (
          !collapsed &&
          (rng.startContainer.nodeType == 3 ||
            (rng.startContainer.nodeType == 1 && domUtils.isEmptyBlock(rng.startContainer)))
        ) {
          if (browser.ie) {
            var span = rng.document.createElement('span')
            rng
              .insertNode(span)
              .setStartBefore(span)
              .collapse(true)
            rng.select()
            domUtils.remove(span)
          } else {
            rng.select()
          }
        }
      }
    })
  }
  UE.plugins['fiximgclick'] = (function() {
    var elementUpdated = false

    function Scale() {
      this.editor = null
      this.resizer = null
      this.cover = null
      this.doc = document
      this.prePos = { x: 0, y: 0 }
      this.startPos = { x: 0, y: 0 }
    }
    ;(function() {
      var rect = [
        [0, 0, -1, -1],
        [0, 0, 0, -1],
        [0, 0, 1, -1],
        [0, 0, -1, 0],
        [0, 0, 1, 0],
        [0, 0, -1, 1],
        [0, 0, 0, 1],
        [0, 0, 1, 1]
      ]
      Scale.prototype = {
        init: function(editor) {
          var me = this
          me.editor = editor
          me.startPos = this.prePos = { x: 0, y: 0 }
          me.dragId = -1
          var hands = []
          var cover = (me.cover = document.createElement('div'))
          var resizer = (me.resizer = document.createElement('div'))
          cover.id = me.editor.ui.id + '_imagescale_cover'
          cover.style.cssText =
            'position:absolute;display:none;z-index:' +
            me.editor.options.zIndex +
            ';filter:alpha(opacity=0); opacity:0;background:#CCC;'
          domUtils.on(cover, 'mousedown click', function() {
            me.hide(true)
          })
          for (i = 0; i < 8; i++) {
            hands.push('<span class="edui-editor-imagescale-hand' + i + '"></span>')
          }
          resizer.id = me.editor.ui.id + '_imagescale'
          resizer.className = 'edui-editor-imagescale'
          resizer.innerHTML = hands.join('')
          resizer.style.cssText += ';display:none;border:1px solid #3b77ff;z-index:' + me.editor.options.zIndex + ';'
          me.editor.ui.getDom().appendChild(cover)
          me.editor.ui.getDom().appendChild(resizer)
          me.initStyle()
          me.initEvents()
        },
        initStyle: function() {
          utils.cssRule(
            'imagescale',
            '.edui-editor-imagescale{display:none;position:absolute;border:1px solid #38B2CE;cursor:hand;-webkit-box-sizing: content-box;-moz-box-sizing: content-box;box-sizing: content-box;}' +
            '.edui-editor-imagescale span{position:absolute;width:6px;height:6px;overflow:hidden;font-size:0px;display:block;background-color:#3C9DD0;}' +
            '.edui-editor-imagescale .edui-editor-imagescale-hand0{cursor:nw-resize;top:0;margin-top:-4px;left:0;margin-left:-4px;}' +
            '.edui-editor-imagescale .edui-editor-imagescale-hand1{cursor:n-resize;top:0;margin-top:-4px;left:50%;margin-left:-4px;}' +
            '.edui-editor-imagescale .edui-editor-imagescale-hand2{cursor:ne-resize;top:0;margin-top:-4px;left:100%;margin-left:-3px;}' +
            '.edui-editor-imagescale .edui-editor-imagescale-hand3{cursor:w-resize;top:50%;margin-top:-4px;left:0;margin-left:-4px;}' +
            '.edui-editor-imagescale .edui-editor-imagescale-hand4{cursor:e-resize;top:50%;margin-top:-4px;left:100%;margin-left:-3px;}' +
            '.edui-editor-imagescale .edui-editor-imagescale-hand5{cursor:sw-resize;top:100%;margin-top:-3px;left:0;margin-left:-4px;}' +
            '.edui-editor-imagescale .edui-editor-imagescale-hand6{cursor:s-resize;top:100%;margin-top:-3px;left:50%;margin-left:-4px;}' +
            '.edui-editor-imagescale .edui-editor-imagescale-hand7{cursor:se-resize;top:100%;margin-top:-3px;left:100%;margin-left:-3px;}'
          )
        },
        initEvents: function() {
          var me = this
          me.startPos.x = me.startPos.y = 0
          me.isDraging = false
        },
        _eventHandler: function(e) {
          var me = this
          switch (e.type) {
            case 'mousedown':
              var hand = e.target || e.srcElement
              var hand
              if (hand.className.indexOf('edui-editor-imagescale-hand') != -1 && me.dragId == -1) {
                me.dragId = hand.className.slice(-1)
                me.startPos.x = me.prePos.x = e.clientX
                me.startPos.y = me.prePos.y = e.clientY
                domUtils.on(me.doc, 'mousemove', me.proxy(me._eventHandler, me))
              }
              break
            case 'mousemove':
              if (me.dragId != -1) {
                me.updateContainerStyle(me.dragId, { x: e.clientX - me.prePos.x, y: e.clientY - me.prePos.y })
                me.prePos.x = e.clientX
                me.prePos.y = e.clientY
                elementUpdated = true
                me.updateTargetElement()
              }
              break
            case 'mouseup':
              if (me.dragId != -1) {
                me.updateContainerStyle(me.dragId, { x: e.clientX - me.prePos.x, y: e.clientY - me.prePos.y })
                me.updateTargetElement()
                if (me.target.parentNode) {
                  me.attachTo(me.target)
                }
                me.dragId = -1
              }
              domUtils.un(me.doc, 'mousemove', me.proxy(me._eventHandler, me))
              if (elementUpdated) {
                elementUpdated = false
                me.editor.fireEvent('contentchange')
              }
              break
            default:
              break
          }
        },
        updateTargetElement: function() {
          var me = this
          domUtils.setStyles(me.target, { width: me.resizer.style.width, height: me.resizer.style.height })
          me.target.width = parseInt(me.resizer.style.width)
          me.target.height = parseInt(me.resizer.style.height)
          me.attachTo(me.target)
        },
        updateContainerStyle: function(dir, offset) {
          var me = this
          var dom = me.resizer
          var tmp
          if (rect[dir][0] != 0) {
            tmp = parseInt(dom.style.left) + offset.x
            dom.style.left = me._validScaledProp('left', tmp) + 'px'
          }
          if (rect[dir][1] != 0) {
            tmp = parseInt(dom.style.top) + offset.y
            dom.style.top = me._validScaledProp('top', tmp) + 'px'
          }
          if (rect[dir][2] != 0) {
            tmp = dom.clientWidth + rect[dir][2] * offset.x
            dom.style.width = me._validScaledProp('width', tmp) + 'px'
          }
          if (rect[dir][3] != 0) {
            tmp = dom.clientHeight + rect[dir][3] * offset.y
            dom.style.height = me._validScaledProp('height', tmp) + 'px'
          }
        },
        _validScaledProp: function(prop, value) {
          var ele = this.resizer
          var wrap = document
          value = isNaN(value) ? 0 : value
          switch (prop) {
            case 'left':
              return value < 0
                ? 0
                : value + ele.clientWidth > wrap.clientWidth
                  ? wrap.clientWidth - ele.clientWidth
                  : value
            case 'top':
              return value < 0
                ? 0
                : value + ele.clientHeight > wrap.clientHeight
                  ? wrap.clientHeight - ele.clientHeight
                  : value
            case 'width':
              return value <= 0
                ? 1
                : value + ele.offsetLeft > wrap.clientWidth
                  ? wrap.clientWidth - ele.offsetLeft
                  : value
            case 'height':
              return value <= 0
                ? 1
                : value + ele.offsetTop > wrap.clientHeight
                  ? wrap.clientHeight - ele.offsetTop
                  : value
          }
        },
        hideCover: function() {
          this.cover.style.display = 'none'
        },
        showCover: function() {
          var me = this
          var editorPos = domUtils.getXY(me.editor.ui.getDom())
          var iframePos = domUtils.getXY(me.editor.iframe)
          domUtils.setStyles(me.cover, {
            width: me.editor.iframe.offsetWidth + 'px',
            height: me.editor.iframe.offsetHeight + 'px',
            top: iframePos.y - editorPos.y + 'px',
            left: iframePos.x - editorPos.x + 'px',
            position: 'absolute',
            display: ''
          })
        },
        show: function(targetObj) {
          var me = this
          me.resizer.style.display = 'block'
          if (targetObj) {
            me.attachTo(targetObj)
          }
          domUtils.on(this.resizer, 'mousedown', me.proxy(me._eventHandler, me))
          domUtils.on(me.doc, 'mouseup', me.proxy(me._eventHandler, me))
          me.showCover()
          me.editor.fireEvent('afterscaleshow', me)
          me.editor.fireEvent('saveScene')
        },
        hide: function(imageOutClick) {
          var me = this
          me.hideCover()
          me.resizer.style.display = 'none'
          domUtils.un(me.resizer, 'mousedown', me.proxy(me._eventHandler, me))
          domUtils.un(me.doc, 'mouseup', me.proxy(me._eventHandler, me))
          me.editor.fireEvent('afterscalehide', me)
          if (imageOutClick) {
            me.editor.setContent(me.editor.getContent())
          }
        },
        proxy: function(fn, context) {
          return function(e) {
            return fn.apply(context || this, arguments)
          }
        },
        attachTo: function(targetObj) {
          var me = this
          var target = (me.target = targetObj)
          var resizer = this.resizer
          var imgPos = domUtils.getXY(target)
          var iframePos = domUtils.getXY(me.editor.iframe)
          var editorPos = domUtils.getXY(resizer.parentNode)
          domUtils.setStyles(resizer, {
            width: target.width + 'px',
            height: target.height + 'px',
            left:
              iframePos.x +
              imgPos.x -
              me.editor.document.body.scrollLeft -
              editorPos.x -
              parseInt(resizer.style.borderLeftWidth) +
              'px',
            top:
              iframePos.y +
              imgPos.y -
              me.editor.document.body.scrollTop -
              editorPos.y -
              parseInt(resizer.style.borderTopWidth) +
              'px'
          })
        }
      }
    })()
    return function() {
      var me = this
      var imageScale
      me.setOpt('imageScaleEnabled', true)
      if (!browser.ie && me.options.imageScaleEnabled) {
        me.addListener('click', function(type, e) {
          var range = me.selection.getRange()
          var img = range.getClosedNode()
          if (img && img.tagName == 'IMG' && me.body.contentEditable != 'false') {
            if (
              img.className.indexOf('edui-faked-music') != -1 ||
              img.getAttribute('anchorname') ||
              domUtils.hasClass(img, 'loadingclass') ||
              domUtils.hasClass(img, 'loaderrorclass')
            ) {
              return
            }
            if (!imageScale) {
              imageScale = new Scale()
              imageScale.init(me)
              me.ui.getDom().appendChild(imageScale.resizer)
              var _keyDownHandler = function(e) {
                imageScale.hide()
                if (imageScale.target) {
                  me.selection
                    .getRange()
                    .selectNode(imageScale.target)
                    .select()
                }
              }
              var _mouseDownHandler = function(e) {
                var ele = e.target || e.srcElement
                if (ele && (ele.className === undefined || ele.className.indexOf('edui-editor-imagescale') == -1)) {
                  _keyDownHandler(e)
                }
              }
              var timer
              me.addListener('afterscaleshow', function(e) {
                me.addListener('beforekeydown', _keyDownHandler)
                me.addListener('beforemousedown', _mouseDownHandler)
                domUtils.on(document, 'keydown', _keyDownHandler)
                domUtils.on(document, 'mousedown', _mouseDownHandler)
                me.selection.getNative().removeAllRanges()
              })
              me.addListener('afterscalehide', function(e) {
                me.removeListener('beforekeydown', _keyDownHandler)
                me.removeListener('beforemousedown', _mouseDownHandler)
                domUtils.un(document, 'keydown', _keyDownHandler)
                domUtils.un(document, 'mousedown', _mouseDownHandler)
                var target = imageScale.target
                if (target.parentNode) {
                  me.selection
                    .getRange()
                    .selectNode(target)
                    .select()
                }
              })
              domUtils.on(imageScale.resizer, 'mousedown', function(e) {
                me.selection.getNative().removeAllRanges()
                var ele = e.target || e.srcElement
                if (ele && ele.className.indexOf('edui-editor-imagescale-hand') == -1) {
                  timer = setTimeout(function() {
                    imageScale.hide()
                    if (imageScale.target) {
                      me.selection
                        .getRange()
                        .selectNode(ele)
                        .select()
                    }
                  }, 200)
                }
              })
              domUtils.on(imageScale.resizer, 'mouseup', function(e) {
                var ele = e.target || e.srcElement
                if (ele && ele.className.indexOf('edui-editor-imagescale-hand') == -1) {
                  clearTimeout(timer)
                }
              })
            }
            imageScale.show(img)
          } else {
            if (imageScale && imageScale.resizer.style.display != 'none') {
              imageScale.hide()
            }
          }
        })
      }
      if (browser.webkit) {
        me.addListener('click', function(type, e) {
          if (e.target.tagName == 'IMG' && me.body.contentEditable != 'false') {
            var range = new dom.Range(me.document)
            range.selectNode(e.target).select()
          }
        })
      }
    }
  })()
  UE.plugin.register(
    'autolink',
    function() {
      var cont = 0
      return !browser.ie
        ? {
          bindEvents: {
            reset: function() {
              cont = 0
            },
            keydown: function(type, evt) {
              var me = this
              var keyCode = evt.keyCode || evt.which
              if (keyCode == 32 || keyCode == 13) {
                var sel = me.selection.getNative()
                var range = sel.getRangeAt(0).cloneRange()
                var offset
                var charCode
                var start = range.startContainer
                while (start.nodeType == 1 && range.startOffset > 0) {
                  start = range.startContainer.childNodes[range.startOffset - 1]
                  if (!start) {
                    break
                  }
                  range.setStart(start, start.nodeType == 1 ? start.childNodes.length : start.nodeValue.length)
                  range.collapse(true)
                  start = range.startContainer
                }
                do {
                  if (range.startOffset == 0) {
                    start = range.startContainer.previousSibling
                    while (start && start.nodeType == 1) {
                      start = start.lastChild
                    }
                    if (!start || domUtils.isFillChar(start)) {
                      break
                    }
                    offset = start.nodeValue.length
                  } else {
                    start = range.startContainer
                    offset = range.startOffset
                  }
                  range.setStart(start, offset - 1)
                  charCode = range.toString().charCodeAt(0)
                } while (charCode != 160 && charCode != 32)
                if (
                  range
                    .toString()
                    .replace(new RegExp(domUtils.fillChar, 'g'), '')
                    .match(/(?:https?:\/\/|ssh:\/\/|ftp:\/\/|file:\/|www\.)/i)
                ) {
                  while (range.toString().length) {
                    if (/^(?:https?:\/\/|ssh:\/\/|ftp:\/\/|file:\/|www\.)/i.test(range.toString())) {
                      break
                    }
                    try {
                      range.setStart(range.startContainer, range.startOffset + 1)
                    } catch (e) {
                      var start = range.startContainer
                      while (!(next = start.nextSibling)) {
                        if (domUtils.isBody(start)) {
                          return
                        }
                        start = start.parentNode
                      }
                      range.setStart(next, 0)
                    }
                  }
                  if (domUtils.findParentByTagName(range.startContainer, 'a', true)) {
                    return
                  }
                  var a = me.document.createElement('a')
                  var text = me.document.createTextNode(' ')
                  var href
                  me.undoManger && me.undoManger.save()
                  a.appendChild(range.extractContents())
                  a.href = a.innerHTML = a.innerHTML.replace(/<[^>]+>/g, '')
                  href = a.getAttribute('href').replace(new RegExp(domUtils.fillChar, 'g'), '')
                  href = /^(?:https?:\/\/)/gi.test(href) ? href : 'http://' + href
                  a.setAttribute('_src', utils.html(href))
                  a.href = utils.html(href)
                  range.insertNode(a)
                  a.parentNode.insertBefore(text, a.nextSibling)
                  range.setStart(text, 0)
                  range.collapse(true)
                  sel.removeAllRanges()
                  sel.addRange(range)
                  me.undoManger && me.undoManger.save()
                }
              }
            }
          }
        }
        : {}
    },
    function() {
      var keyCodes = { 37: 1, 38: 1, 39: 1, 40: 1, 13: 1, 32: 1 }

      function checkIsCludeLink(node) {
        if (node.nodeType == 3) {
          return null
        }
        if (node.nodeName == 'A') {
          return node
        }
        var lastChild = node.lastChild
        while (lastChild) {
          if (lastChild.nodeName == 'A') {
            return lastChild
          }
          if (lastChild.nodeType == 3) {
            if (domUtils.isWhitespace(lastChild)) {
              lastChild = lastChild.previousSibling
              continue
            }
            return null
          }
          lastChild = lastChild.lastChild
        }
      }

      browser.ie &&
      this.addListener('keyup', function(cmd, evt) {
        var me = this
        var keyCode = evt.keyCode
        if (keyCodes[keyCode]) {
          var rng = me.selection.getRange()
          var start = rng.startContainer
          if (keyCode == 13) {
            while (start && !domUtils.isBody(start) && !domUtils.isBlockElm(start)) {
              start = start.parentNode
            }
            if (start && !domUtils.isBody(start) && start.nodeName == 'P') {
              var pre = start.previousSibling
              if (pre && pre.nodeType == 1) {
                var pre = checkIsCludeLink(pre)
                if (pre && !pre.getAttribute('_href')) {
                  domUtils.remove(pre, true)
                }
              }
            }
          } else {
            if (keyCode == 32) {
              if (start.nodeType == 3 && /^\s$/.test(start.nodeValue)) {
                start = start.previousSibling
                if (start && start.nodeName == 'A' && !start.getAttribute('_href')) {
                  domUtils.remove(start, true)
                }
              }
            } else {
              start = domUtils.findParentByTagName(start, 'a', true)
              if (start && !start.getAttribute('_href')) {
                var bk = rng.createBookmark()
                domUtils.remove(start, true)
                rng.moveToBookmark(bk).select(true)
              }
            }
          }
        }
      })
    }
  )
  UE.plugins['autoheight'] = function() {
    var me = this
    me.autoHeightEnabled = me.options.autoHeightEnabled !== false
    if (!me.autoHeightEnabled) {
      return
    }
    var bakOverflow
    var lastHeight = 0
    var options = me.options
    var currentHeight
    var timer

    function adjustHeight() {
      var me = this
      clearTimeout(timer)
      if (isFullscreen) {
        return
      }
      if (!me.queryCommandState || (me.queryCommandState && me.queryCommandState('source') != 1)) {
        timer = setTimeout(function() {
          var node = me.body.lastChild
          while (node && node.nodeType != 1) {
            node = node.previousSibling
          }
          if (node && node.nodeType == 1) {
            node.style.clear = 'both'
            currentHeight = Math.max(
              domUtils.getXY(node).y + node.offsetHeight + 25,
              Math.max(options.minFrameHeight, options.initialFrameHeight)
            )
            if (currentHeight != lastHeight) {
              if (currentHeight !== parseInt(me.iframe.parentNode.style.height)) {
                me.iframe.parentNode.style.height = currentHeight + 'px'
              }
              me.body.style.height = currentHeight + 'px'
              lastHeight = currentHeight
            }
            domUtils.removeStyle(node, 'clear')
          }
        }, 50)
      }
    }

    var isFullscreen
    me.addListener('fullscreenchanged', function(cmd, f) {
      isFullscreen = f
    })
    me.addListener('destroy', function() {
      me.removeListener('contentchange afterinserthtml keyup mouseup', adjustHeight)
    })
    me.enableAutoHeight = function() {
      var me = this
      if (!me.autoHeightEnabled) {
        return
      }
      var doc = me.document
      me.autoHeightEnabled = true
      bakOverflow = doc.body.style.overflowY
      doc.body.style.overflowY = 'hidden'
      me.addListener('contentchange afterinserthtml keyup mouseup', adjustHeight)
      setTimeout(
        function() {
          adjustHeight.call(me)
        },
        browser.gecko ? 100 : 0
      )
      me.fireEvent('autoheightchanged', me.autoHeightEnabled)
    }
    me.disableAutoHeight = function() {
      me.body.style.overflowY = bakOverflow || ''
      me.removeListener('contentchange', adjustHeight)
      me.removeListener('keyup', adjustHeight)
      me.removeListener('mouseup', adjustHeight)
      me.autoHeightEnabled = false
      me.fireEvent('autoheightchanged', me.autoHeightEnabled)
    }
    me.on('setHeight', function() {
      me.disableAutoHeight()
    })
    me.addListener('ready', function() {
      me.enableAutoHeight()
      var timer
      domUtils.on(browser.ie ? me.body : me.document, browser.webkit ? 'dragover' : 'drop', function() {
        clearTimeout(timer)
        timer = setTimeout(function() {
          adjustHeight.call(me)
        }, 100)
      })
      var lastScrollY
      window.onscroll = function() {
        if (lastScrollY === null) {
          lastScrollY = this.scrollY
        } else {
          if (this.scrollY == 0 && lastScrollY != 0) {
            me.window.scrollTo(0, 0)
            lastScrollY = null
          }
        }
      }
    })
  }
  UE.plugins['autofloat'] = function() {
    var me = this
    var lang = me.getLang()
    me.setOpt({ topOffset: 0 })
    var optsAutoFloatEnabled = me.options.autoFloatEnabled !== false
    var topOffset = me.options.topOffset
    if (!optsAutoFloatEnabled) {
      return
    }
    var uiUtils = UE.ui.uiUtils
    var LteIE6 = browser.ie && browser.version <= 6
    var quirks = browser.quirks

    function checkHasUI() {
      if (!UE.ui) {
        alert(lang.autofloatMsg)
        return 0
      }
      return 1
    }

    function fixIE6FixedPos() {
      var docStyle = document.body.style
      docStyle.backgroundImage = 'url("about:blank")'
      docStyle.backgroundAttachment = 'fixed'
    }

    var bakCssText
    var placeHolder = document.createElement('div')
    var toolbarBox
    var orgTop
    var getPosition
    var flag = true

    function setFloating() {
      var toobarBoxPos = domUtils.getXY(toolbarBox)
      var origalFloat = domUtils.getComputedStyle(toolbarBox, 'position')
      var origalLeft = domUtils.getComputedStyle(toolbarBox, 'left')
      toolbarBox.style.width = toolbarBox.offsetWidth + 'px'
      toolbarBox.style.zIndex = me.options.zIndex * 1 + 1
      toolbarBox.parentNode.insertBefore(placeHolder, toolbarBox)
      if (LteIE6 || (quirks && browser.ie)) {
        if (toolbarBox.style.position != 'absolute') {
          toolbarBox.style.position = 'absolute'
        }
        toolbarBox.style.top =
          (document.body.scrollTop || document.documentElement.scrollTop) - orgTop + topOffset + 'px'
      } else {
        if (browser.ie7Compat && flag) {
          flag = false
          toolbarBox.style.left =
            domUtils.getXY(toolbarBox).x - document.documentElement.getBoundingClientRect().left + 2 + 'px'
        }
        if (toolbarBox.style.position != 'fixed') {
          toolbarBox.style.position = 'fixed'
          toolbarBox.style.top = topOffset + 'px'
          ;(origalFloat == 'absolute' || origalFloat == 'relative') &&
          parseFloat(origalLeft) &&
          (toolbarBox.style.left = toobarBoxPos.x + 'px')
        }
      }
    }

    function unsetFloating() {
      flag = true
      if (placeHolder.parentNode) {
        placeHolder.parentNode.removeChild(placeHolder)
      }
      toolbarBox.style.cssText = bakCssText
    }

    function updateFloating() {
      var rect3 = getPosition(me.container)
      var offset = me.options.toolbarTopOffset || 0
      if (rect3.top < 0 && rect3.bottom - toolbarBox.offsetHeight > offset) {
        setFloating()
      } else {
        unsetFloating()
      }
    }

    var defer_updateFloating = utils.defer(
      function() {
        updateFloating()
      },
      browser.ie ? 200 : 100,
      true
    )
    me.addListener('destroy', function() {
      domUtils.un(window, ['scroll', 'resize'], updateFloating)
      me.removeListener('keydown', defer_updateFloating)
    })
    me.addListener('ready', function() {
      if (checkHasUI(me)) {
        if (!me.ui) {
          return
        }
        getPosition = uiUtils.getClientRect
        toolbarBox = me.ui.getDom('toolbarbox')
        orgTop = getPosition(toolbarBox).top
        bakCssText = toolbarBox.style.cssText
        placeHolder.style.height = toolbarBox.offsetHeight + 'px'
        if (LteIE6) {
          fixIE6FixedPos()
        }
        domUtils.on(window, ['scroll', 'resize'], updateFloating)
        me.addListener('keydown', defer_updateFloating)
        me.addListener('beforefullscreenchange', function(t, enabled) {
          if (enabled) {
            unsetFloating()
          }
        })
        me.addListener('fullscreenchanged', function(t, enabled) {
          if (!enabled) {
            updateFloating()
          }
        })
        me.addListener('sourcemodechanged', function(t, enabled) {
          setTimeout(function() {
            updateFloating()
          }, 0)
        })
        me.addListener('clearDoc', function() {
          setTimeout(function() {
            updateFloating()
          }, 0)
        })
      }
    })
  }
  UE.plugins['video'] = function() {
    var me = this

    function creatInsertStr(url, width, height, id, align, classname, type) {
      url = utils.unhtmlForUrl(url)
      align = utils.unhtml(align)
      classname = utils.unhtml(classname)
      width = parseInt(width, 10) || 0
      height = parseInt(height, 10) || 0
      var str
      switch (type) {
        case 'image':
          str =
            '<img ' +
            (id ? 'id="' + id + '"' : '') +
            ' width="' +
            width +
            '" height="' +
            height +
            '" _url="' +
            url +
            '" class="' +
            classname.replace(/\bvideo-js\b/, '') +
            '"' +
            ' src="' +
            me.options.UEDITOR_HOME_URL +
            'themes/default/images/spacer.gif" style="background:url(' +
            me.options.UEDITOR_HOME_URL +
            'themes/default/images/videologo.gif) no-repeat center center; border:1px solid gray;' +
            (align ? 'float:' + align + ';' : '') +
            '" />'
          break
        case 'embed':
          str =
            '<embed type="application/x-shockwave-flash" class="' +
            classname +
            '" pluginspage="http://www.macromedia.com/go/getflashplayer"' +
            ' src="' +
            utils.html(url) +
            '" width="' +
            width +
            '" height="' +
            height +
            '"' +
            (align ? ' style="float:' + align + '"' : '') +
            ' wmode="transparent" play="true" loop="false" menu="false" allowscriptaccess="never" allowfullscreen="true" >'
          break
        case 'video':
          var ext = url.substr(url.lastIndexOf('.') + 1)
          if (ext == 'ogv') {
            ext = 'ogg'
          }
          str =
            '<video style="background: #000;" ' +
            (id ? ' id="' + id + '"' : '') +
            ' class="' +
            classname +
            ' video-js" ' +
            (align ? ' style="float:' + align + '"' : '') +
            ' controls preload="none" width="' +
            width +
            '" height="' +
            height +
            '" src="' +
            url +
            '" data-setup="{}">' +
            '<source src="' +
            url +
            '" type="video/' +
            ext +
            '" /></video>'
          break
      }
      return str
    }

    function switchImgAndVideo(root, img2video) {
      utils.each(root.getNodesByTagName(img2video ? 'img' : 'embed video'), function(node) {
        var className = node.getAttr('class')
        if (className && className.indexOf('edui-faked-video') != -1) {
          var html = creatInsertStr(
            img2video ? node.getAttr('_url') : node.getAttr('src'),
            node.getAttr('width'),
            node.getAttr('height'),
            null,
            node.getStyle('float') || '',
            className,
            img2video ? 'embed' : 'image'
          )
          node.parentNode.replaceChild(UE.uNode.createElement(html), node)
        }
        if (className && className.indexOf('edui-upload-video') != -1) {
          var html = creatInsertStr(
            img2video ? node.getAttr('_url') : node.getAttr('src'),
            node.getAttr('width'),
            node.getAttr('height'),
            null,
            node.getStyle('float') || '',
            className,
            img2video ? 'video' : 'image'
          )
          node.parentNode.replaceChild(UE.uNode.createElement(html), node)
        }
      })
    }

    me.addOutputRule(function(root) {
      switchImgAndVideo(root, true)
    })
    me.addInputRule(function(root) {
      switchImgAndVideo(root)
    })
    me.commands['insertvideo'] = {
      execCommand: function(cmd, videoObjs, type) {
        videoObjs = utils.isArray(videoObjs) ? videoObjs : [videoObjs]
        var html = []
        var id = 'tmpVedio'
        var cl
        for (var i = 0, vi, len = videoObjs.length; i < len; i++) {
          vi = videoObjs[i]
          cl = type == 'upload' ? 'edui-upload-video video-js vjs-default-skin' : 'edui-faked-video'
          html.push(creatInsertStr(vi.url, vi.width || 420, vi.height || 280, id + i, null, cl, 'video'))
        }
        me.execCommand('inserthtml', html.join(''), true)
        var rng = this.selection.getRange()
        for (var i = 0, len = videoObjs.length; i < len; i++) {
          var img = this.document.getElementById('tmpVedio' + i)
          domUtils.removeAttributes(img, 'id')
          rng.selectNode(img).select()
          me.execCommand('imagefloat', videoObjs[i].align)
        }
      },
      queryCommandState: function() {
        var img = me.selection.getRange().getClosedNode()
        var flag = img && (img.className == 'edui-faked-video' || img.className.indexOf('edui-upload-video') != -1)
        return flag ? 1 : 0
      }
    }
  }
  ;(function() {
    var UETable = (UE.UETable = function(table) {
      this.table = table
      this.indexTable = []
      this.selectedTds = []
      this.cellsRange = {}
      this.update(table)
    })
    UETable.removeSelectedClass = function(cells) {
      utils.each(cells, function(cell) {
        domUtils.removeClasses(cell, 'selectTdClass')
      })
    }
    UETable.addSelectedClass = function(cells) {
      utils.each(cells, function(cell) {
        domUtils.addClass(cell, 'selectTdClass')
      })
    }
    UETable.isEmptyBlock = function(node) {
      var reg = new RegExp(domUtils.fillChar, 'g')
      if (node[browser.ie ? 'innerText' : 'textContent'].replace(/^\s*$/, '').replace(reg, '').length > 0) {
        return 0
      }
      for (var i in dtd.$isNotEmpty) {
        if (dtd.$isNotEmpty.hasOwnProperty(i)) {
          if (node.getElementsByTagName(i).length) {
            return 0
          }
        }
      }
      return 1
    }
    UETable.getWidth = function(cell) {
      if (!cell) {
        return 0
      }
      return parseInt(domUtils.getComputedStyle(cell, 'width'), 10)
    }
    UETable.getTableCellAlignState = function(cells) {
      !utils.isArray(cells) && (cells = [cells])
      var result = {}
      var status = ['align', 'valign']
      var tempStatus = null
      var isSame = true
      utils.each(cells, function(cellNode) {
        utils.each(status, function(currentState) {
          tempStatus = cellNode.getAttribute(currentState)
          if (!result[currentState] && tempStatus) {
            result[currentState] = tempStatus
          } else {
            if (!result[currentState] || tempStatus !== result[currentState]) {
              isSame = false
              return false
            }
          }
        })
        return isSame
      })
      return isSame ? result : null
    }
    UETable.getTableItemsByRange = function(editor) {
      var start = editor.selection.getStart()
      if (start && start.id && start.id.indexOf('_baidu_bookmark_start_') === 0 && start.nextSibling) {
        start = start.nextSibling
      }
      var cell = start && domUtils.findParentByTagName(start, ['td', 'th'], true)
      var tr = cell && cell.parentNode
      var caption = start && domUtils.findParentByTagName(start, 'caption', true)
      var table = caption ? caption.parentNode : tr && tr.parentNode.parentNode
      return { cell: cell, tr: tr, table: table, caption: caption }
    }
    UETable.getUETableBySelected = function(editor) {
      var table = UETable.getTableItemsByRange(editor).table
      if (table && table.ueTable && table.ueTable.selectedTds.length) {
        return table.ueTable
      }
      return null
    }
    UETable.getDefaultValue = function(editor, table) {
      var borderMap = { thin: '0px', medium: '1px', thick: '2px' }
      var tableBorder
      var tdPadding
      var tdBorder
      var tmpValue
      if (!table) {
        table = editor.document.createElement('table')
        table.insertRow(0).insertCell(0).innerHTML = 'xxx'
        editor.body.appendChild(table)
        var td = table.getElementsByTagName('td')[0]
        tmpValue = domUtils.getComputedStyle(table, 'border-left-width')
        tableBorder = parseInt(borderMap[tmpValue] || tmpValue, 10)
        tmpValue = domUtils.getComputedStyle(td, 'padding-left')
        tdPadding = parseInt(borderMap[tmpValue] || tmpValue, 10)
        tmpValue = domUtils.getComputedStyle(td, 'border-left-width')
        tdBorder = parseInt(borderMap[tmpValue] || tmpValue, 10)
        domUtils.remove(table)
        return { tableBorder: tableBorder, tdPadding: tdPadding, tdBorder: tdBorder }
      } else {
        td = table.getElementsByTagName('td')[0]
        tmpValue = domUtils.getComputedStyle(table, 'border-left-width')
        tableBorder = parseInt(borderMap[tmpValue] || tmpValue, 10)
        tmpValue = domUtils.getComputedStyle(td, 'padding-left')
        tdPadding = parseInt(borderMap[tmpValue] || tmpValue, 10)
        tmpValue = domUtils.getComputedStyle(td, 'border-left-width')
        tdBorder = parseInt(borderMap[tmpValue] || tmpValue, 10)
        return { tableBorder: tableBorder, tdPadding: tdPadding, tdBorder: tdBorder }
      }
    }
    UETable.getUETable = function(tdOrTable) {
      var tag = tdOrTable.tagName.toLowerCase()
      tdOrTable =
        tag == 'td' || tag == 'th' || tag == 'caption'
          ? domUtils.findParentByTagName(tdOrTable, 'table', true)
          : tdOrTable
      if (!tdOrTable.ueTable) {
        tdOrTable.ueTable = new UETable(tdOrTable)
      }
      return tdOrTable.ueTable
    }
    UETable.cloneCell = function(cell, ignoreMerge, keepPro) {
      if (!cell || utils.isString(cell)) {
        return this.table.ownerDocument.createElement(cell || 'td')
      }
      var flag = domUtils.hasClass(cell, 'selectTdClass')
      flag && domUtils.removeClasses(cell, 'selectTdClass')
      var tmpCell = cell.cloneNode(true)
      if (ignoreMerge) {
        tmpCell.rowSpan = tmpCell.colSpan = 1
      }
      !keepPro && domUtils.removeAttributes(tmpCell, 'width height')
      !keepPro && domUtils.removeAttributes(tmpCell, 'style')
      tmpCell.style.borderLeftStyle = ''
      tmpCell.style.borderTopStyle = ''
      tmpCell.style.borderLeftColor = cell.style.borderRightColor
      tmpCell.style.borderLeftWidth = cell.style.borderRightWidth
      tmpCell.style.borderTopColor = cell.style.borderBottomColor
      tmpCell.style.borderTopWidth = cell.style.borderBottomWidth
      flag && domUtils.addClass(cell, 'selectTdClass')
      return tmpCell
    }
    UETable.prototype = {
      getMaxRows: function() {
        var rows = this.table.rows
        var maxLen = 1
        for (var i = 0, row; (row = rows[i]); i++) {
          var currentMax = 1
          for (var j = 0, cj; (cj = row.cells[j++]);) {
            currentMax = Math.max(cj.rowSpan || 1, currentMax)
          }
          maxLen = Math.max(currentMax + i, maxLen)
        }
        return maxLen
      },
      getMaxCols: function() {
        var rows = this.table.rows
        var maxLen = 0
        var cellRows = {}
        for (var i = 0, row; (row = rows[i]); i++) {
          var cellsNum = 0
          for (var j = 0, cj; (cj = row.cells[j++]);) {
            cellsNum += cj.colSpan || 1
            if (cj.rowSpan && cj.rowSpan > 1) {
              for (var k = 1; k < cj.rowSpan; k++) {
                if (!cellRows['row_' + (i + k)]) {
                  cellRows['row_' + (i + k)] = cj.colSpan || 1
                } else {
                  cellRows['row_' + (i + k)]++
                }
              }
            }
          }
          cellsNum += cellRows['row_' + i] || 0
          maxLen = Math.max(cellsNum, maxLen)
        }
        return maxLen
      },
      getCellColIndex: function(cell) {
      },
      getHSideCell: function(cell, right) {
        try {
          var cellInfo = this.getCellInfo(cell)
          var previewRowIndex
          var previewColIndex
          var len = this.selectedTds.length
          var range = this.cellsRange
          if (
            (!right && (!len ? !cellInfo.colIndex : !range.beginColIndex)) ||
            (right && (!len ? cellInfo.colIndex == this.colsNum - 1 : range.endColIndex == this.colsNum - 1))
          ) {
            return null
          }
          previewRowIndex = !len ? cellInfo.rowIndex : range.beginRowIndex
          previewColIndex = !right
            ? !len
              ? cellInfo.colIndex < 1
                ? 0
                : cellInfo.colIndex - 1
              : range.beginColIndex - 1
            : !len
              ? cellInfo.colIndex + 1
              : range.endColIndex + 1
          return this.getCell(
            this.indexTable[previewRowIndex][previewColIndex].rowIndex,
            this.indexTable[previewRowIndex][previewColIndex].cellIndex
          )
        } catch (e) {
          showError(e)
        }
      },
      getTabNextCell: function(cell, preRowIndex) {
        var cellInfo = this.getCellInfo(cell)
        var rowIndex = preRowIndex || cellInfo.rowIndex
        var colIndex = cellInfo.colIndex + 1 + (cellInfo.colSpan - 1)
        var nextCell
        try {
          nextCell = this.getCell(
            this.indexTable[rowIndex][colIndex].rowIndex,
            this.indexTable[rowIndex][colIndex].cellIndex
          )
        } catch (e) {
          try {
            rowIndex = rowIndex * 1 + 1
            colIndex = 0
            nextCell = this.getCell(
              this.indexTable[rowIndex][colIndex].rowIndex,
              this.indexTable[rowIndex][colIndex].cellIndex
            )
          } catch (e) {
          }
        }
        return nextCell
      },
      getVSideCell: function(cell, bottom, ignoreRange) {
        try {
          var cellInfo = this.getCellInfo(cell)
          var nextRowIndex
          var nextColIndex
          var len = this.selectedTds.length && !ignoreRange
          var range = this.cellsRange
          if (
            (!bottom && cellInfo.rowIndex == 0) ||
            (bottom &&
              (!len ? cellInfo.rowIndex + cellInfo.rowSpan > this.rowsNum - 1 : range.endRowIndex == this.rowsNum - 1))
          ) {
            return null
          }
          nextRowIndex = !bottom
            ? !len
              ? cellInfo.rowIndex - 1
              : range.beginRowIndex - 1
            : !len
              ? cellInfo.rowIndex + cellInfo.rowSpan
              : range.endRowIndex + 1
          nextColIndex = !len ? cellInfo.colIndex : range.beginColIndex
          return this.getCell(
            this.indexTable[nextRowIndex][nextColIndex].rowIndex,
            this.indexTable[nextRowIndex][nextColIndex].cellIndex
          )
        } catch (e) {
          showError(e)
        }
      },
      getSameEndPosCells: function(cell, xOrY) {
        try {
          var flag = xOrY.toLowerCase() === 'x'
          var end = domUtils.getXY(cell)[flag ? 'x' : 'y'] + cell['offset' + (flag ? 'Width' : 'Height')]
          var rows = this.table.rows
          var cells = null
          var returns = []
          for (var i = 0; i < this.rowsNum; i++) {
            cells = rows[i].cells
            for (var j = 0, tmpCell; (tmpCell = cells[j++]);) {
              var tmpEnd = domUtils.getXY(tmpCell)[flag ? 'x' : 'y'] + tmpCell['offset' + (flag ? 'Width' : 'Height')]
              if (tmpEnd > end && flag) {
                break
              }
              if (cell == tmpCell || end == tmpEnd) {
                if (tmpCell[flag ? 'colSpan' : 'rowSpan'] == 1) {
                  returns.push(tmpCell)
                }
                if (flag) {
                  break
                }
              }
            }
          }
          return returns
        } catch (e) {
          showError(e)
        }
      },
      setCellContent: function(cell, content) {
        cell.innerHTML = content || (browser.ie ? domUtils.fillChar : '<br />')
      },
      cloneCell: UETable.cloneCell,
      getSameStartPosXCells: function(cell) {
        try {
          var start = domUtils.getXY(cell).x + cell.offsetWidth
          var rows = this.table.rows
          var cells
          var returns = []
          for (var i = 0; i < this.rowsNum; i++) {
            cells = rows[i].cells
            for (var j = 0, tmpCell; (tmpCell = cells[j++]);) {
              var tmpStart = domUtils.getXY(tmpCell).x
              if (tmpStart > start) {
                break
              }
              if (tmpStart == start && tmpCell.colSpan == 1) {
                returns.push(tmpCell)
                break
              }
            }
          }
          return returns
        } catch (e) {
          showError(e)
        }
      },
      update: function(table) {
        this.table = table || this.table
        this.selectedTds = []
        this.cellsRange = {}
        this.indexTable = []
        var rows = this.table.rows
        var rowsNum = this.getMaxRows()
        var dNum = rowsNum - rows.length
        var colsNum = this.getMaxCols()
        while (dNum--) {
          this.table.insertRow(rows.length)
        }
        this.rowsNum = rowsNum
        this.colsNum = colsNum
        for (var i = 0, len = rows.length; i < len; i++) {
          this.indexTable[i] = new Array(colsNum)
        }
        for (var rowIndex = 0, row; (row = rows[rowIndex]); rowIndex++) {
          for (var cellIndex = 0, cell, cells = row.cells; (cell = cells[cellIndex]); cellIndex++) {
            if (cell.rowSpan > rowsNum) {
              cell.rowSpan = rowsNum
            }
            var colIndex = cellIndex
            var rowSpan = cell.rowSpan || 1
            var colSpan = cell.colSpan || 1
            while (this.indexTable[rowIndex][colIndex]) {
              colIndex++
            }
            for (var j = 0; j < rowSpan; j++) {
              for (var k = 0; k < colSpan; k++) {
                this.indexTable[rowIndex + j][colIndex + k] = {
                  rowIndex: rowIndex,
                  cellIndex: cellIndex,
                  colIndex: colIndex,
                  rowSpan: rowSpan,
                  colSpan: colSpan
                }
              }
            }
          }
        }
        for (j = 0; j < rowsNum; j++) {
          for (k = 0; k < colsNum; k++) {
            if (this.indexTable[j][k] === undefined) {
              row = rows[j]
              cell = row.cells[row.cells.length - 1]
              cell = cell ? cell.cloneNode(true) : this.table.ownerDocument.createElement('td')
              this.setCellContent(cell)
              if (cell.colSpan !== 1) {
                cell.colSpan = 1
              }
              if (cell.rowSpan !== 1) {
                cell.rowSpan = 1
              }
              row.appendChild(cell)
              this.indexTable[j][k] = { rowIndex: j, cellIndex: cell.cellIndex, colIndex: k, rowSpan: 1, colSpan: 1 }
            }
          }
        }
        var tds = domUtils.getElementsByTagName(this.table, 'td')
        var selectTds = []
        utils.each(tds, function(td) {
          if (domUtils.hasClass(td, 'selectTdClass')) {
            selectTds.push(td)
          }
        })
        if (selectTds.length) {
          var start = selectTds[0]
          var end = selectTds[selectTds.length - 1]
          var startInfo = this.getCellInfo(start)
          var endInfo = this.getCellInfo(end)
          this.selectedTds = selectTds
          this.cellsRange = {
            beginRowIndex: startInfo.rowIndex,
            beginColIndex: startInfo.colIndex,
            endRowIndex: endInfo.rowIndex + endInfo.rowSpan - 1,
            endColIndex: endInfo.colIndex + endInfo.colSpan - 1
          }
        }
        if (!domUtils.hasClass(this.table.rows[0], 'firstRow')) {
          domUtils.addClass(this.table.rows[0], 'firstRow')
          for (var i = 1; i < this.table.rows.length; i++) {
            domUtils.removeClasses(this.table.rows[i], 'firstRow')
          }
        }
      },
      getCellInfo: function(cell) {
        if (!cell) {
          return
        }
        var cellIndex = cell.cellIndex
        var rowIndex = cell.parentNode.rowIndex
        var rowInfo = this.indexTable[rowIndex]
        var numCols = this.colsNum
        for (var colIndex = cellIndex; colIndex < numCols; colIndex++) {
          var cellInfo = rowInfo[colIndex]
          if (cellInfo.rowIndex === rowIndex && cellInfo.cellIndex === cellIndex) {
            return cellInfo
          }
        }
      },
      getCell: function(rowIndex, cellIndex) {
        return (rowIndex < this.rowsNum && this.table.rows[rowIndex].cells[cellIndex]) || null
      },
      deleteCell: function(cell, rowIndex) {
        rowIndex = typeof rowIndex === 'number' ? rowIndex : cell.parentNode.rowIndex
        var row = this.table.rows[rowIndex]
        row.deleteCell(cell.cellIndex)
      },
      getCellsRange: function(cellA, cellB) {
        function checkRange(beginRowIndex, beginColIndex, endRowIndex, endColIndex) {
          var tmpBeginRowIndex = beginRowIndex
          var tmpBeginColIndex = beginColIndex
          var tmpEndRowIndex = endRowIndex
          var tmpEndColIndex = endColIndex
          var cellInfo
          var colIndex
          var rowIndex
          if (beginRowIndex > 0) {
            for (colIndex = beginColIndex; colIndex < endColIndex; colIndex++) {
              cellInfo = me.indexTable[beginRowIndex][colIndex]
              rowIndex = cellInfo.rowIndex
              if (rowIndex < beginRowIndex) {
                tmpBeginRowIndex = Math.min(rowIndex, tmpBeginRowIndex)
              }
            }
          }
          if (endColIndex < me.colsNum) {
            for (rowIndex = beginRowIndex; rowIndex < endRowIndex; rowIndex++) {
              cellInfo = me.indexTable[rowIndex][endColIndex]
              colIndex = cellInfo.colIndex + cellInfo.colSpan - 1
              if (colIndex > endColIndex) {
                tmpEndColIndex = Math.max(colIndex, tmpEndColIndex)
              }
            }
          }
          if (endRowIndex < me.rowsNum) {
            for (colIndex = beginColIndex; colIndex < endColIndex; colIndex++) {
              cellInfo = me.indexTable[endRowIndex][colIndex]
              rowIndex = cellInfo.rowIndex + cellInfo.rowSpan - 1
              if (rowIndex > endRowIndex) {
                tmpEndRowIndex = Math.max(rowIndex, tmpEndRowIndex)
              }
            }
          }
          if (beginColIndex > 0) {
            for (rowIndex = beginRowIndex; rowIndex < endRowIndex; rowIndex++) {
              cellInfo = me.indexTable[rowIndex][beginColIndex]
              colIndex = cellInfo.colIndex
              if (colIndex < beginColIndex) {
                tmpBeginColIndex = Math.min(cellInfo.colIndex, tmpBeginColIndex)
              }
            }
          }
          if (
            tmpBeginRowIndex != beginRowIndex ||
            tmpBeginColIndex != beginColIndex ||
            tmpEndRowIndex != endRowIndex ||
            tmpEndColIndex != endColIndex
          ) {
            return checkRange(tmpBeginRowIndex, tmpBeginColIndex, tmpEndRowIndex, tmpEndColIndex)
          } else {
            return {
              beginRowIndex: beginRowIndex,
              beginColIndex: beginColIndex,
              endRowIndex: endRowIndex,
              endColIndex: endColIndex
            }
          }
        }

        try {
          var me = this
          var cellAInfo = me.getCellInfo(cellA)
          if (cellA === cellB) {
            return {
              beginRowIndex: cellAInfo.rowIndex,
              beginColIndex: cellAInfo.colIndex,
              endRowIndex: cellAInfo.rowIndex + cellAInfo.rowSpan - 1,
              endColIndex: cellAInfo.colIndex + cellAInfo.colSpan - 1
            }
          }
          var cellBInfo = me.getCellInfo(cellB)
          var beginRowIndex = Math.min(cellAInfo.rowIndex, cellBInfo.rowIndex)
          var beginColIndex = Math.min(cellAInfo.colIndex, cellBInfo.colIndex)
          var endRowIndex = Math.max(
            cellAInfo.rowIndex + cellAInfo.rowSpan - 1,
            cellBInfo.rowIndex + cellBInfo.rowSpan - 1
          )
          var endColIndex = Math.max(
            cellAInfo.colIndex + cellAInfo.colSpan - 1,
            cellBInfo.colIndex + cellBInfo.colSpan - 1
          )
          return checkRange(beginRowIndex, beginColIndex, endRowIndex, endColIndex)
        } catch (e) {
        }
      },
      getCells: function(range) {
        this.clearSelected()
        var beginRowIndex = range.beginRowIndex
        var beginColIndex = range.beginColIndex
        var endRowIndex = range.endRowIndex
        var endColIndex = range.endColIndex
        var cellInfo
        var rowIndex
        var colIndex
        var tdHash = {}
        var returnTds = []
        for (var i = beginRowIndex; i <= endRowIndex; i++) {
          for (var j = beginColIndex; j <= endColIndex; j++) {
            cellInfo = this.indexTable[i][j]
            rowIndex = cellInfo.rowIndex
            colIndex = cellInfo.colIndex
            var key = rowIndex + '|' + colIndex
            if (tdHash[key]) {
              continue
            }
            tdHash[key] = 1
            if (
              rowIndex < i ||
              colIndex < j ||
              rowIndex + cellInfo.rowSpan - 1 > endRowIndex ||
              colIndex + cellInfo.colSpan - 1 > endColIndex
            ) {
              return null
            }
            returnTds.push(this.getCell(rowIndex, cellInfo.cellIndex))
          }
        }
        return returnTds
      },
      clearSelected: function() {
        UETable.removeSelectedClass(this.selectedTds)
        this.selectedTds = []
        this.cellsRange = {}
      },
      setSelected: function(range) {
        var cells = this.getCells(range)
        UETable.addSelectedClass(cells)
        this.selectedTds = cells
        this.cellsRange = range
      },
      isFullRow: function() {
        var range = this.cellsRange
        return range.endColIndex - range.beginColIndex + 1 == this.colsNum
      },
      isFullCol: function() {
        var range = this.cellsRange
        var table = this.table
        var ths = table.getElementsByTagName('th')
        var rows = range.endRowIndex - range.beginRowIndex + 1
        return !ths.length ? rows == this.rowsNum : rows == this.rowsNum || rows == this.rowsNum - 1
      },
      getNextCell: function(cell, bottom, ignoreRange) {
        try {
          var cellInfo = this.getCellInfo(cell)
          var nextRowIndex
          var nextColIndex
          var len = this.selectedTds.length && !ignoreRange
          var range = this.cellsRange
          if (
            (!bottom && cellInfo.rowIndex == 0) ||
            (bottom &&
              (!len ? cellInfo.rowIndex + cellInfo.rowSpan > this.rowsNum - 1 : range.endRowIndex == this.rowsNum - 1))
          ) {
            return null
          }
          nextRowIndex = !bottom
            ? !len
              ? cellInfo.rowIndex - 1
              : range.beginRowIndex - 1
            : !len
              ? cellInfo.rowIndex + cellInfo.rowSpan
              : range.endRowIndex + 1
          nextColIndex = !len ? cellInfo.colIndex : range.beginColIndex
          return this.getCell(
            this.indexTable[nextRowIndex][nextColIndex].rowIndex,
            this.indexTable[nextRowIndex][nextColIndex].cellIndex
          )
        } catch (e) {
          showError(e)
        }
      },
      getPreviewCell: function(cell, top) {
        try {
          var cellInfo = this.getCellInfo(cell)
          var previewRowIndex
          var previewColIndex
          var len = this.selectedTds.length
          var range = this.cellsRange
          if (
            (!top && (!len ? !cellInfo.colIndex : !range.beginColIndex)) ||
            (top && (!len ? cellInfo.rowIndex > this.colsNum - 1 : range.endColIndex == this.colsNum - 1))
          ) {
            return null
          }
          previewRowIndex = !top
            ? !len
              ? cellInfo.rowIndex
              : range.beginRowIndex
            : !len
              ? cellInfo.rowIndex < 1
                ? 0
                : cellInfo.rowIndex - 1
              : range.beginRowIndex
          previewColIndex = !top
            ? !len
              ? cellInfo.colIndex < 1
                ? 0
                : cellInfo.colIndex - 1
              : range.beginColIndex - 1
            : !len
              ? cellInfo.colIndex
              : range.endColIndex + 1
          return this.getCell(
            this.indexTable[previewRowIndex][previewColIndex].rowIndex,
            this.indexTable[previewRowIndex][previewColIndex].cellIndex
          )
        } catch (e) {
          showError(e)
        }
      },
      moveContent: function(cellTo, cellFrom) {
        if (UETable.isEmptyBlock(cellFrom)) {
          return
        }
        if (UETable.isEmptyBlock(cellTo)) {
          cellTo.innerHTML = cellFrom.innerHTML
          return
        }
        var child = cellTo.lastChild
        if (child.nodeType == 3 || !dtd.$block[child.tagName]) {
          cellTo.appendChild(cellTo.ownerDocument.createElement('br'))
        }
        while ((child = cellFrom.firstChild)) {
          cellTo.appendChild(child)
        }
      },
      mergeRight: function(cell) {
        var cellInfo = this.getCellInfo(cell)
        var rightColIndex = cellInfo.colIndex + cellInfo.colSpan
        var rightCellInfo = this.indexTable[cellInfo.rowIndex][rightColIndex]
        var rightCell = this.getCell(rightCellInfo.rowIndex, rightCellInfo.cellIndex)
        cell.colSpan = cellInfo.colSpan + rightCellInfo.colSpan
        cell.removeAttribute('width')
        this.moveContent(cell, rightCell)
        this.deleteCell(rightCell, rightCellInfo.rowIndex)
        this.update()
      },
      mergeDown: function(cell) {
        var cellInfo = this.getCellInfo(cell)
        var downRowIndex = cellInfo.rowIndex + cellInfo.rowSpan
        var downCellInfo = this.indexTable[downRowIndex][cellInfo.colIndex]
        var downCell = this.getCell(downCellInfo.rowIndex, downCellInfo.cellIndex)
        cell.rowSpan = cellInfo.rowSpan + downCellInfo.rowSpan
        cell.removeAttribute('height')
        this.moveContent(cell, downCell)
        this.deleteCell(downCell, downCellInfo.rowIndex)
        this.update()
      },
      mergeRange: function() {
        var range = this.cellsRange
        var leftTopCell = this.getCell(
          range.beginRowIndex,
          this.indexTable[range.beginRowIndex][range.beginColIndex].cellIndex
        )
        if (leftTopCell.tagName == 'TH' && range.endRowIndex !== range.beginRowIndex) {
          var index = this.indexTable
          var info = this.getCellInfo(leftTopCell)
          leftTopCell = this.getCell(1, index[1][info.colIndex].cellIndex)
          range = this.getCellsRange(
            leftTopCell,
            this.getCell(
              index[this.rowsNum - 1][info.colIndex].rowIndex,
              index[this.rowsNum - 1][info.colIndex].cellIndex
            )
          )
        }
        var cells = this.getCells(range)
        for (var i = 0, ci; (ci = cells[i++]);) {
          if (ci !== leftTopCell) {
            this.moveContent(leftTopCell, ci)
            this.deleteCell(ci)
          }
        }
        leftTopCell.rowSpan = range.endRowIndex - range.beginRowIndex + 1
        leftTopCell.rowSpan > 1 && leftTopCell.removeAttribute('height')
        leftTopCell.colSpan = range.endColIndex - range.beginColIndex + 1
        leftTopCell.colSpan > 1 && leftTopCell.removeAttribute('width')
        if (leftTopCell.rowSpan == this.rowsNum && leftTopCell.colSpan != 1) {
          leftTopCell.colSpan = 1
        }
        if (leftTopCell.colSpan == this.colsNum && leftTopCell.rowSpan != 1) {
          var rowIndex = leftTopCell.parentNode.rowIndex
          if (this.table.deleteRow) {
            for (var i = rowIndex + 1, curIndex = rowIndex + 1, len = leftTopCell.rowSpan; i < len; i++) {
              this.table.deleteRow(curIndex)
            }
          } else {
            for (var i = 0, len = leftTopCell.rowSpan - 1; i < len; i++) {
              var row = this.table.rows[rowIndex + 1]
              row.parentNode.removeChild(row)
            }
          }
          leftTopCell.rowSpan = 1
        }
        this.update()
      },
      insertRow: function(rowIndex, sourceCell) {
        var numCols = this.colsNum
        var table = this.table
        var row = table.insertRow(rowIndex)
        var cell
        var isInsertTitle = typeof sourceCell === 'string' && sourceCell.toUpperCase() == 'TH'

        function replaceTdToTh(colIndex, cell, tableRow) {
          if (colIndex == 0) {
            var tr = tableRow.nextSibling || tableRow.previousSibling
            var th = tr.cells[colIndex]
            if (th.tagName == 'TH') {
              th = cell.ownerDocument.createElement('th')
              th.appendChild(cell.firstChild)
              tableRow.insertBefore(th, cell)
              domUtils.remove(cell)
            }
          } else {
            if (cell.tagName == 'TH') {
              var td = cell.ownerDocument.createElement('td')
              td.appendChild(cell.firstChild)
              tableRow.insertBefore(td, cell)
              domUtils.remove(cell)
            }
          }
        }

        if (rowIndex == 0 || rowIndex == this.rowsNum) {
          for (var colIndex = 0; colIndex < numCols; colIndex++) {
            cell = this.cloneCell(sourceCell, true)
            this.setCellContent(cell)
            cell.getAttribute('vAlign') && cell.setAttribute('vAlign', cell.getAttribute('vAlign'))
            row.appendChild(cell)
            if (!isInsertTitle) {
              replaceTdToTh(colIndex, cell, row)
            }
          }
        } else {
          var infoRow = this.indexTable[rowIndex]
          var cellIndex = 0
          for (colIndex = 0; colIndex < numCols; colIndex++) {
            var cellInfo = infoRow[colIndex]
            if (cellInfo.rowIndex < rowIndex) {
              cell = this.getCell(cellInfo.rowIndex, cellInfo.cellIndex)
              cell.rowSpan = cellInfo.rowSpan + 1
            } else {
              cell = this.cloneCell(sourceCell, true)
              this.setCellContent(cell)
              row.appendChild(cell)
            }
            if (!isInsertTitle) {
              replaceTdToTh(colIndex, cell, row)
            }
          }
        }
        this.update()
        return row
      },
      deleteRow: function(rowIndex) {
        var row = this.table.rows[rowIndex]
        var infoRow = this.indexTable[rowIndex]
        var colsNum = this.colsNum
        var count = 0
        for (var colIndex = 0; colIndex < colsNum;) {
          var cellInfo = infoRow[colIndex]
          var cell = this.getCell(cellInfo.rowIndex, cellInfo.cellIndex)
          if (cell.rowSpan > 1) {
            if (cellInfo.rowIndex == rowIndex) {
              var clone = cell.cloneNode(true)
              clone.rowSpan = cell.rowSpan - 1
              clone.innerHTML = ''
              cell.rowSpan = 1
              var nextRowIndex = rowIndex + 1
              var nextRow = this.table.rows[nextRowIndex]
              var insertCellIndex
              var preMerged = this.getPreviewMergedCellsNum(nextRowIndex, colIndex) - count
              if (preMerged < colIndex) {
                insertCellIndex = colIndex - preMerged - 1
                domUtils.insertAfter(nextRow.cells[insertCellIndex], clone)
              } else {
                if (nextRow.cells.length) {
                  nextRow.insertBefore(clone, nextRow.cells[0])
                }
              }
              count += 1
            }
          }
          colIndex += cell.colSpan || 1
        }
        var deleteTds = []
        var cacheMap = {}
        for (colIndex = 0; colIndex < colsNum; colIndex++) {
          var tmpRowIndex = infoRow[colIndex].rowIndex
          var tmpCellIndex = infoRow[colIndex].cellIndex
          var key = tmpRowIndex + '_' + tmpCellIndex
          if (cacheMap[key]) {
            continue
          }
          cacheMap[key] = 1
          cell = this.getCell(tmpRowIndex, tmpCellIndex)
          deleteTds.push(cell)
        }
        var mergeTds = []
        utils.each(deleteTds, function(td) {
          if (td.rowSpan == 1) {
            td.parentNode.removeChild(td)
          } else {
            mergeTds.push(td)
          }
        })
        utils.each(mergeTds, function(td) {
          td.rowSpan--
        })
        row.parentNode.removeChild(row)
        this.update()
      },
      insertCol: function(colIndex, sourceCell, defaultValue) {
        var rowsNum = this.rowsNum
        var rowIndex = 0
        var tableRow
        var cell
        var backWidth = parseInt(
          (this.table.offsetWidth - (this.colsNum + 1) * 20 - (this.colsNum + 1)) / (this.colsNum + 1),
          10
        )
        var isInsertTitleCol = typeof sourceCell === 'string' && sourceCell.toUpperCase() == 'TH'

        function replaceTdToTh(rowIndex, cell, tableRow) {
          if (rowIndex == 0) {
            var th = cell.nextSibling || cell.previousSibling
            if (th.tagName == 'TH') {
              th = cell.ownerDocument.createElement('th')
              th.appendChild(cell.firstChild)
              tableRow.insertBefore(th, cell)
              domUtils.remove(cell)
            }
          } else {
            if (cell.tagName == 'TH') {
              var td = cell.ownerDocument.createElement('td')
              td.appendChild(cell.firstChild)
              tableRow.insertBefore(td, cell)
              domUtils.remove(cell)
            }
          }
        }

        var preCell
        if (colIndex == 0 || colIndex == this.colsNum) {
          for (; rowIndex < rowsNum; rowIndex++) {
            tableRow = this.table.rows[rowIndex]
            preCell = tableRow.cells[colIndex == 0 ? colIndex : tableRow.cells.length]
            cell = this.cloneCell(sourceCell, true)
            this.setCellContent(cell)
            cell.setAttribute('vAlign', cell.getAttribute('vAlign'))
            preCell && cell.setAttribute('width', preCell.getAttribute('width'))
            if (!colIndex) {
              tableRow.insertBefore(cell, tableRow.cells[0])
            } else {
              domUtils.insertAfter(tableRow.cells[tableRow.cells.length - 1], cell)
            }
            if (!isInsertTitleCol) {
              replaceTdToTh(rowIndex, cell, tableRow)
            }
          }
        } else {
          for (; rowIndex < rowsNum; rowIndex++) {
            var cellInfo = this.indexTable[rowIndex][colIndex]
            if (cellInfo.colIndex < colIndex) {
              cell = this.getCell(cellInfo.rowIndex, cellInfo.cellIndex)
              cell.colSpan = cellInfo.colSpan + 1
            } else {
              tableRow = this.table.rows[rowIndex]
              preCell = tableRow.cells[cellInfo.cellIndex]
              cell = this.cloneCell(sourceCell, true)
              this.setCellContent(cell)
              cell.setAttribute('vAlign', cell.getAttribute('vAlign'))
              preCell && cell.setAttribute('width', preCell.getAttribute('width'))
              preCell ? tableRow.insertBefore(cell, preCell) : tableRow.appendChild(cell)
            }
            if (!isInsertTitleCol) {
              replaceTdToTh(rowIndex, cell, tableRow)
            }
          }
        }
        this.update()
        this.updateWidth(backWidth, defaultValue || { tdPadding: 10, tdBorder: 1 })
      },
      updateWidth: function(width, defaultValue) {
        var table = this.table
        var tmpWidth = UETable.getWidth(table) - defaultValue.tdPadding * 2 - defaultValue.tdBorder + width
        if (tmpWidth < table.ownerDocument.body.offsetWidth) {
          table.setAttribute('width', tmpWidth)
          return
        }
        var tds = domUtils.getElementsByTagName(this.table, 'td th')
        utils.each(tds, function(td) {
          td.setAttribute('width', width)
        })
      },
      deleteCol: function(colIndex) {
        var indexTable = this.indexTable
        var tableRows = this.table.rows
        var backTableWidth = this.table.getAttribute('width')
        var backTdWidth = 0
        var rowsNum = this.rowsNum
        var cacheMap = {}
        for (var rowIndex = 0; rowIndex < rowsNum;) {
          var infoRow = indexTable[rowIndex]
          var cellInfo = infoRow[colIndex]
          var key = cellInfo.rowIndex + '_' + cellInfo.colIndex
          if (cacheMap[key]) {
            continue
          }
          cacheMap[key] = 1
          var cell = this.getCell(cellInfo.rowIndex, cellInfo.cellIndex)
          if (!backTdWidth) {
            backTdWidth = cell && parseInt(cell.offsetWidth / cell.colSpan, 10).toFixed(0)
          }
          if (cell.colSpan > 1) {
            cell.colSpan--
          } else {
            tableRows[rowIndex].deleteCell(cellInfo.cellIndex)
          }
          rowIndex += cellInfo.rowSpan || 1
        }
        this.table.setAttribute('width', backTableWidth - backTdWidth)
        this.update()
      },
      splitToCells: function(cell) {
        var me = this
        var cells = this.splitToRows(cell)
        utils.each(cells, function(cell) {
          me.splitToCols(cell)
        })
      },
      splitToRows: function(cell) {
        var cellInfo = this.getCellInfo(cell)
        var rowIndex = cellInfo.rowIndex
        var colIndex = cellInfo.colIndex
        var results = []
        cell.rowSpan = 1
        results.push(cell)
        for (var i = rowIndex, endRow = rowIndex + cellInfo.rowSpan; i < endRow; i++) {
          if (i == rowIndex) {
            continue
          }
          var tableRow = this.table.rows[i]
          var tmpCell = tableRow.insertCell(colIndex - this.getPreviewMergedCellsNum(i, colIndex))
          tmpCell.colSpan = cellInfo.colSpan
          this.setCellContent(tmpCell)
          tmpCell.setAttribute('vAlign', cell.getAttribute('vAlign'))
          tmpCell.setAttribute('align', cell.getAttribute('align'))
          if (cell.style.cssText) {
            tmpCell.style.cssText = cell.style.cssText
          }
          results.push(tmpCell)
        }
        this.update()
        return results
      },
      getPreviewMergedCellsNum: function(rowIndex, colIndex) {
        var indexRow = this.indexTable[rowIndex]
        var num = 0
        for (var i = 0; i < colIndex;) {
          var colSpan = indexRow[i].colSpan
          var tmpRowIndex = indexRow[i].rowIndex
          num += colSpan - (tmpRowIndex == rowIndex ? 1 : 0)
          i += colSpan
        }
        return num
      },
      splitToCols: function(cell) {
        var backWidth = (cell.offsetWidth / cell.colSpan - 22).toFixed(0)
        var cellInfo = this.getCellInfo(cell)
        var rowIndex = cellInfo.rowIndex
        var colIndex = cellInfo.colIndex
        var results = []
        cell.colSpan = 1
        cell.setAttribute('width', backWidth)
        results.push(cell)
        for (var j = colIndex, endCol = colIndex + cellInfo.colSpan; j < endCol; j++) {
          if (j == colIndex) {
            continue
          }
          var tableRow = this.table.rows[rowIndex]
          var tmpCell = tableRow.insertCell(this.indexTable[rowIndex][j].cellIndex + 1)
          tmpCell.rowSpan = cellInfo.rowSpan
          this.setCellContent(tmpCell)
          tmpCell.setAttribute('vAlign', cell.getAttribute('vAlign'))
          tmpCell.setAttribute('align', cell.getAttribute('align'))
          tmpCell.setAttribute('width', backWidth)
          if (cell.style.cssText) {
            tmpCell.style.cssText = cell.style.cssText
          }
          if (cell.tagName == 'TH') {
            var th = cell.ownerDocument.createElement('th')
            th.appendChild(tmpCell.firstChild)
            th.setAttribute('vAlign', cell.getAttribute('vAlign'))
            th.rowSpan = tmpCell.rowSpan
            tableRow.insertBefore(th, tmpCell)
            domUtils.remove(tmpCell)
          }
          results.push(tmpCell)
        }
        this.update()
        return results
      },
      isLastCell: function(cell, rowsNum, colsNum) {
        rowsNum = rowsNum || this.rowsNum
        colsNum = colsNum || this.colsNum
        var cellInfo = this.getCellInfo(cell)
        return cellInfo.rowIndex + cellInfo.rowSpan == rowsNum && cellInfo.colIndex + cellInfo.colSpan == colsNum
      },
      getLastCell: function(cells) {
        cells = cells || this.table.getElementsByTagName('td')
        var firstInfo = this.getCellInfo(cells[0])
        var me = this
        var last = cells[0]
        var tr = last.parentNode
        var cellsNum = 0
        var cols = 0
        var rows
        utils.each(cells, function(cell) {
          if (cell.parentNode == tr) {
            cols += cell.colSpan || 1
          }
          cellsNum += cell.rowSpan * cell.colSpan || 1
        })
        rows = cellsNum / cols
        utils.each(cells, function(cell) {
          if (me.isLastCell(cell, rows, cols)) {
            last = cell
            return false
          }
        })
        return last
      },
      selectRow: function(rowIndex) {
        var indexRow = this.indexTable[rowIndex]
        var start = this.getCell(indexRow[0].rowIndex, indexRow[0].cellIndex)
        var end = this.getCell(indexRow[this.colsNum - 1].rowIndex, indexRow[this.colsNum - 1].cellIndex)
        var range = this.getCellsRange(start, end)
        this.setSelected(range)
      },
      selectTable: function() {
        var tds = this.table.getElementsByTagName('td')
        var range = this.getCellsRange(tds[0], tds[tds.length - 1])
        this.setSelected(range)
      },
      setBackground: function(cells, value) {
        if (typeof value === 'string') {
          utils.each(cells, function(cell) {
            cell.style.backgroundColor = value
          })
        } else {
          if (typeof value === 'object') {
            value = utils.extend({ repeat: true, colorList: ['#ddd', '#fff'] }, value)
            var rowIndex = this.getCellInfo(cells[0]).rowIndex
            var count = 0
            var colors = value.colorList
            var getColor = function(list, index, repeat) {
              return list[index] ? list[index] : repeat ? list[index % list.length] : ''
            }
            for (var i = 0, cell; (cell = cells[i++]);) {
              var cellInfo = this.getCellInfo(cell)
              cell.style.backgroundColor = getColor(
                colors,
                rowIndex + count == cellInfo.rowIndex ? count : ++count,
                value.repeat
              )
            }
          }
        }
      },
      removeBackground: function(cells) {
        utils.each(cells, function(cell) {
          cell.style.backgroundColor = ''
        })
      }
    }

    function showError(e) {
    }
  })()
  ;(function() {
    var UT = UE.UETable
    var getTableItemsByRange = function(editor) {
      return UT.getTableItemsByRange(editor)
    }
    var getUETableBySelected = function(editor) {
      return UT.getUETableBySelected(editor)
    }
    var getDefaultValue = function(editor, table) {
      return UT.getDefaultValue(editor, table)
    }
    var getUETable = function(tdOrTable) {
      return UT.getUETable(tdOrTable)
    }
    UE.commands['inserttable'] = {
      queryCommandState: function() {
        return getTableItemsByRange(this).table ? -1 : 0
      },
      execCommand: function(cmd, opt) {
        function createTable(opt, tdWidth) {
          var html = []
          var rowsNum = opt.numRows
          var colsNum = opt.numCols
          for (var r = 0; r < rowsNum; r++) {
            html.push('<tr' + (r == 0 ? ' class="firstRow"' : '') + '>')
            for (var c = 0; c < colsNum; c++) {
              html.push(
                '<td width="' +
                tdWidth +
                '"  vAlign="' +
                opt.tdvalign +
                '" >' +
                (browser.ie && browser.version < 11 ? domUtils.fillChar : '<br/>') +
                '</td>'
              )
            }
            html.push('</tr>')
          }
          return '<table><tbody>' + html.join('') + '</tbody></table>'
        }

        if (!opt) {
          opt = utils.extend(
            {},
            { numCols: this.options.defaultCols, numRows: this.options.defaultRows, tdvalign: this.options.tdvalign }
          )
        }
        var me = this
        var range = this.selection.getRange()
        var start = range.startContainer
        var firstParentBlock =
          domUtils.findParent(
            start,
            function(node) {
              return domUtils.isBlockElm(node)
            },
            true
          ) || me.body
        var defaultValue = getDefaultValue(me)
        var tableWidth = firstParentBlock.offsetWidth
        var tdWidth = Math.floor(tableWidth / opt.numCols - defaultValue.tdPadding * 2 - defaultValue.tdBorder)
        !opt.tdvalign && (opt.tdvalign = me.options.tdvalign)
        me.execCommand('inserthtml', createTable(opt, tdWidth))
      }
    }
    UE.commands['insertparagraphbeforetable'] = {
      queryCommandState: function() {
        return getTableItemsByRange(this).cell ? 0 : -1
      },
      execCommand: function() {
        var table = getTableItemsByRange(this).table
        if (table) {
          var p = this.document.createElement('p')
          p.innerHTML = browser.ie ? '&nbsp;' : '<br />'
          table.parentNode.insertBefore(p, table)
          this.selection
            .getRange()
            .setStart(p, 0)
            .setCursor()
        }
      }
    }
    UE.commands['deletetable'] = {
      queryCommandState: function() {
        var rng = this.selection.getRange()
        return domUtils.findParentByTagName(rng.startContainer, 'table', true) ? 0 : -1
      },
      execCommand: function(cmd, table) {
        var rng = this.selection.getRange()
        table = table || domUtils.findParentByTagName(rng.startContainer, 'table', true)
        if (table) {
          var next = table.nextSibling
          if (!next) {
            next = domUtils.createElement(this.document, 'p', { innerHTML: browser.ie ? domUtils.fillChar : '<br/>' })
            table.parentNode.insertBefore(next, table)
          }
          domUtils.remove(table)
          rng = this.selection.getRange()
          if (next.nodeType == 3) {
            rng.setStartBefore(next)
          } else {
            rng.setStart(next, 0)
          }
          rng.setCursor(false, true)
          this.fireEvent('tablehasdeleted')
        }
      }
    }
    UE.commands['cellalign'] = {
      queryCommandState: function() {
        return getSelectedArr(this).length ? 0 : -1
      },
      execCommand: function(cmd, align) {
        var selectedTds = getSelectedArr(this)
        if (selectedTds.length) {
          for (var i = 0, ci; (ci = selectedTds[i++]);) {
            ci.setAttribute('align', align)
          }
        }
      }
    }
    UE.commands['cellvalign'] = {
      queryCommandState: function() {
        return getSelectedArr(this).length ? 0 : -1
      },
      execCommand: function(cmd, valign) {
        var selectedTds = getSelectedArr(this)
        if (selectedTds.length) {
          for (var i = 0, ci; (ci = selectedTds[i++]);) {
            ci.setAttribute('vAlign', valign)
          }
        }
      }
    }
    UE.commands['insertcaption'] = {
      queryCommandState: function() {
        var table = getTableItemsByRange(this).table
        if (table) {
          return table.getElementsByTagName('caption').length == 0 ? 1 : -1
        }
        return -1
      },
      execCommand: function() {
        var table = getTableItemsByRange(this).table
        if (table) {
          var caption = this.document.createElement('caption')
          caption.innerHTML = browser.ie ? domUtils.fillChar : '<br/>'
          table.insertBefore(caption, table.firstChild)
          var range = this.selection.getRange()
          range.setStart(caption, 0).setCursor()
        }
      }
    }
    UE.commands['deletecaption'] = {
      queryCommandState: function() {
        var rng = this.selection.getRange()
        var table = domUtils.findParentByTagName(rng.startContainer, 'table')
        if (table) {
          return table.getElementsByTagName('caption').length == 0 ? -1 : 1
        }
        return -1
      },
      execCommand: function() {
        var rng = this.selection.getRange()
        var table = domUtils.findParentByTagName(rng.startContainer, 'table')
        if (table) {
          domUtils.remove(table.getElementsByTagName('caption')[0])
          var range = this.selection.getRange()
          range.setStart(table.rows[0].cells[0], 0).setCursor()
        }
      }
    }
    UE.commands['inserttitle'] = {
      queryCommandState: function() {
        var table = getTableItemsByRange(this).table
        if (table) {
          var firstRow = table.rows[0]
          return firstRow.cells[firstRow.cells.length - 1].tagName.toLowerCase() != 'th' ? 0 : -1
        }
        return -1
      },
      execCommand: function() {
        var table = getTableItemsByRange(this).table
        if (table) {
          getUETable(table).insertRow(0, 'th')
        }
        var th = table.getElementsByTagName('th')[0]
        this.selection
          .getRange()
          .setStart(th, 0)
          .setCursor(false, true)
      }
    }
    UE.commands['deletetitle'] = {
      queryCommandState: function() {
        var table = getTableItemsByRange(this).table
        if (table) {
          var firstRow = table.rows[0]
          return firstRow.cells[firstRow.cells.length - 1].tagName.toLowerCase() == 'th' ? 0 : -1
        }
        return -1
      },
      execCommand: function() {
        var table = getTableItemsByRange(this).table
        if (table) {
          domUtils.remove(table.rows[0])
        }
        var td = table.getElementsByTagName('td')[0]
        this.selection
          .getRange()
          .setStart(td, 0)
          .setCursor(false, true)
      }
    }
    UE.commands['inserttitlecol'] = {
      queryCommandState: function() {
        var table = getTableItemsByRange(this).table
        if (table) {
          var lastRow = table.rows[table.rows.length - 1]
          return lastRow.getElementsByTagName('th').length ? -1 : 0
        }
        return -1
      },
      execCommand: function(cmd) {
        var table = getTableItemsByRange(this).table
        if (table) {
          getUETable(table).insertCol(0, 'th')
        }
        resetTdWidth(table, this)
        var th = table.getElementsByTagName('th')[0]
        this.selection
          .getRange()
          .setStart(th, 0)
          .setCursor(false, true)
      }
    }
    UE.commands['deletetitlecol'] = {
      queryCommandState: function() {
        var table = getTableItemsByRange(this).table
        if (table) {
          var lastRow = table.rows[table.rows.length - 1]
          return lastRow.getElementsByTagName('th').length ? 0 : -1
        }
        return -1
      },
      execCommand: function() {
        var table = getTableItemsByRange(this).table
        if (table) {
          for (var i = 0; i < table.rows.length; i++) {
            domUtils.remove(table.rows[i].children[0])
          }
        }
        resetTdWidth(table, this)
        var td = table.getElementsByTagName('td')[0]
        this.selection
          .getRange()
          .setStart(td, 0)
          .setCursor(false, true)
      }
    }
    UE.commands['mergeright'] = {
      queryCommandState: function(cmd) {
        var tableItems = getTableItemsByRange(this)
        var table = tableItems.table
        var cell = tableItems.cell
        if (!table || !cell) {
          return -1
        }
        var ut = getUETable(table)
        if (ut.selectedTds.length) {
          return -1
        }
        var cellInfo = ut.getCellInfo(cell)
        var rightColIndex = cellInfo.colIndex + cellInfo.colSpan
        if (rightColIndex >= ut.colsNum) {
          return -1
        }
        var rightCellInfo = ut.indexTable[cellInfo.rowIndex][rightColIndex]
        var rightCell = table.rows[rightCellInfo.rowIndex].cells[rightCellInfo.cellIndex]
        if (!rightCell || cell.tagName != rightCell.tagName) {
          return -1
        }
        return rightCellInfo.rowIndex == cellInfo.rowIndex && rightCellInfo.rowSpan == cellInfo.rowSpan ? 0 : -1
      },
      execCommand: function(cmd) {
        var rng = this.selection.getRange()
        var bk = rng.createBookmark(true)
        var cell = getTableItemsByRange(this).cell
        var ut = getUETable(cell)
        ut.mergeRight(cell)
        rng.moveToBookmark(bk).select()
      }
    }
    UE.commands['mergedown'] = {
      queryCommandState: function(cmd) {
        var tableItems = getTableItemsByRange(this)
        var table = tableItems.table
        var cell = tableItems.cell
        if (!table || !cell) {
          return -1
        }
        var ut = getUETable(table)
        if (ut.selectedTds.length) {
          return -1
        }
        var cellInfo = ut.getCellInfo(cell)
        var downRowIndex = cellInfo.rowIndex + cellInfo.rowSpan
        if (downRowIndex >= ut.rowsNum) {
          return -1
        }
        var downCellInfo = ut.indexTable[downRowIndex][cellInfo.colIndex]
        var downCell = table.rows[downCellInfo.rowIndex].cells[downCellInfo.cellIndex]
        if (!downCell || cell.tagName != downCell.tagName) {
          return -1
        }
        return downCellInfo.colIndex == cellInfo.colIndex && downCellInfo.colSpan == cellInfo.colSpan ? 0 : -1
      },
      execCommand: function() {
        var rng = this.selection.getRange()
        var bk = rng.createBookmark(true)
        var cell = getTableItemsByRange(this).cell
        var ut = getUETable(cell)
        ut.mergeDown(cell)
        rng.moveToBookmark(bk).select()
      }
    }
    UE.commands['mergecells'] = {
      queryCommandState: function() {
        return getUETableBySelected(this) ? 0 : -1
      },
      execCommand: function() {
        var ut = getUETableBySelected(this)
        if (ut && ut.selectedTds.length) {
          var cell = ut.selectedTds[0]
          ut.mergeRange()
          var rng = this.selection.getRange()
          if (domUtils.isEmptyBlock(cell)) {
            rng.setStart(cell, 0).collapse(true)
          } else {
            rng.selectNodeContents(cell)
          }
          rng.select()
        }
      }
    }
    UE.commands['insertrow'] = {
      queryCommandState: function() {
        var tableItems = getTableItemsByRange(this)
        var cell = tableItems.cell
        return cell &&
        (cell.tagName == 'TD' || (cell.tagName == 'TH' && tableItems.tr !== tableItems.table.rows[0])) &&
        getUETable(tableItems.table).rowsNum < this.options.maxRowNum
          ? 0
          : -1
      },
      execCommand: function() {
        var rng = this.selection.getRange()
        var bk = rng.createBookmark(true)
        var tableItems = getTableItemsByRange(this)
        var cell = tableItems.cell
        var table = tableItems.table
        var ut = getUETable(table)
        var cellInfo = ut.getCellInfo(cell)
        if (!ut.selectedTds.length) {
          ut.insertRow(cellInfo.rowIndex, cell)
        } else {
          var range = ut.cellsRange
          for (var i = 0, len = range.endRowIndex - range.beginRowIndex + 1; i < len; i++) {
            ut.insertRow(range.beginRowIndex, cell)
          }
        }
        rng.moveToBookmark(bk).select()
        if (table.getAttribute('interlaced') === 'enabled') {
          this.fireEvent('interlacetable', table)
        }
      }
    }
    UE.commands['insertrownext'] = {
      queryCommandState: function() {
        var tableItems = getTableItemsByRange(this)
        var cell = tableItems.cell
        return cell && cell.tagName == 'TD' && getUETable(tableItems.table).rowsNum < this.options.maxRowNum ? 0 : -1
      },
      execCommand: function() {
        var rng = this.selection.getRange()
        var bk = rng.createBookmark(true)
        var tableItems = getTableItemsByRange(this)
        var cell = tableItems.cell
        var table = tableItems.table
        var ut = getUETable(table)
        var cellInfo = ut.getCellInfo(cell)
        if (!ut.selectedTds.length) {
          ut.insertRow(cellInfo.rowIndex + cellInfo.rowSpan, cell)
        } else {
          var range = ut.cellsRange
          for (var i = 0, len = range.endRowIndex - range.beginRowIndex + 1; i < len; i++) {
            ut.insertRow(range.endRowIndex + 1, cell)
          }
        }
        rng.moveToBookmark(bk).select()
        if (table.getAttribute('interlaced') === 'enabled') {
          this.fireEvent('interlacetable', table)
        }
      }
    }
    UE.commands['deleterow'] = {
      queryCommandState: function() {
        var tableItems = getTableItemsByRange(this)
        return tableItems.cell ? 0 : -1
      },
      execCommand: function() {
        var cell = getTableItemsByRange(this).cell
        var ut = getUETable(cell)
        var cellsRange = ut.cellsRange
        var cellInfo = ut.getCellInfo(cell)
        var preCell = ut.getVSideCell(cell)
        var nextCell = ut.getVSideCell(cell, true)
        var rng = this.selection.getRange()
        if (utils.isEmptyObject(cellsRange)) {
          ut.deleteRow(cellInfo.rowIndex)
        } else {
          for (var i = cellsRange.beginRowIndex; i < cellsRange.endRowIndex + 1; i++) {
            ut.deleteRow(cellsRange.beginRowIndex)
          }
        }
        var table = ut.table
        if (!table.getElementsByTagName('td').length) {
          var nextSibling = table.nextSibling
          domUtils.remove(table)
          if (nextSibling) {
            rng.setStart(nextSibling, 0).setCursor(false, true)
          }
        } else {
          if (cellInfo.rowSpan == 1 || cellInfo.rowSpan == cellsRange.endRowIndex - cellsRange.beginRowIndex + 1) {
            if (nextCell || preCell) {
              rng.selectNodeContents(nextCell || preCell).setCursor(false, true)
            }
          } else {
            var newCell = ut.getCell(cellInfo.rowIndex, ut.indexTable[cellInfo.rowIndex][cellInfo.colIndex].cellIndex)
            if (newCell) {
              rng.selectNodeContents(newCell).setCursor(false, true)
            }
          }
        }
        if (table.getAttribute('interlaced') === 'enabled') {
          this.fireEvent('interlacetable', table)
        }
      }
    }
    UE.commands['insertcol'] = {
      queryCommandState: function(cmd) {
        var tableItems = getTableItemsByRange(this)
        var cell = tableItems.cell
        return cell &&
        (cell.tagName == 'TD' || (cell.tagName == 'TH' && cell !== tableItems.tr.cells[0])) &&
        getUETable(tableItems.table).colsNum < this.options.maxColNum
          ? 0
          : -1
      },
      execCommand: function(cmd) {
        var rng = this.selection.getRange()
        var bk = rng.createBookmark(true)
        if (this.queryCommandState(cmd) == -1) {
          return
        }
        var cell = getTableItemsByRange(this).cell
        var ut = getUETable(cell)
        var cellInfo = ut.getCellInfo(cell)
        if (!ut.selectedTds.length) {
          ut.insertCol(cellInfo.colIndex, cell)
        } else {
          var range = ut.cellsRange
          for (var i = 0, len = range.endColIndex - range.beginColIndex + 1; i < len; i++) {
            ut.insertCol(range.beginColIndex, cell)
          }
        }
        rng.moveToBookmark(bk).select(true)
      }
    }
    UE.commands['insertcolnext'] = {
      queryCommandState: function() {
        var tableItems = getTableItemsByRange(this)
        var cell = tableItems.cell
        return cell && getUETable(tableItems.table).colsNum < this.options.maxColNum ? 0 : -1
      },
      execCommand: function() {
        var rng = this.selection.getRange()
        var bk = rng.createBookmark(true)
        var cell = getTableItemsByRange(this).cell
        var ut = getUETable(cell)
        var cellInfo = ut.getCellInfo(cell)
        if (!ut.selectedTds.length) {
          ut.insertCol(cellInfo.colIndex + cellInfo.colSpan, cell)
        } else {
          var range = ut.cellsRange
          for (var i = 0, len = range.endColIndex - range.beginColIndex + 1; i < len; i++) {
            ut.insertCol(range.endColIndex + 1, cell)
          }
        }
        rng.moveToBookmark(bk).select()
      }
    }
    UE.commands['deletecol'] = {
      queryCommandState: function() {
        var tableItems = getTableItemsByRange(this)
        return tableItems.cell ? 0 : -1
      },
      execCommand: function() {
        var cell = getTableItemsByRange(this).cell
        var ut = getUETable(cell)
        var range = ut.cellsRange
        var cellInfo = ut.getCellInfo(cell)
        var preCell = ut.getHSideCell(cell)
        var nextCell = ut.getHSideCell(cell, true)
        if (utils.isEmptyObject(range)) {
          ut.deleteCol(cellInfo.colIndex)
        } else {
          for (var i = range.beginColIndex; i < range.endColIndex + 1; i++) {
            ut.deleteCol(range.beginColIndex)
          }
        }
        var table = ut.table
        var rng = this.selection.getRange()
        if (!table.getElementsByTagName('td').length) {
          var nextSibling = table.nextSibling
          domUtils.remove(table)
          if (nextSibling) {
            rng.setStart(nextSibling, 0).setCursor(false, true)
          }
        } else {
          if (domUtils.inDoc(cell, this.document)) {
            rng.setStart(cell, 0).setCursor(false, true)
          } else {
            if (nextCell && domUtils.inDoc(nextCell, this.document)) {
              rng.selectNodeContents(nextCell).setCursor(false, true)
            } else {
              if (preCell && domUtils.inDoc(preCell, this.document)) {
                rng.selectNodeContents(preCell).setCursor(true, true)
              }
            }
          }
        }
      }
    }
    UE.commands['splittocells'] = {
      queryCommandState: function() {
        var tableItems = getTableItemsByRange(this)
        var cell = tableItems.cell
        if (!cell) {
          return -1
        }
        var ut = getUETable(tableItems.table)
        if (ut.selectedTds.length > 0) {
          return -1
        }
        return cell && (cell.colSpan > 1 || cell.rowSpan > 1) ? 0 : -1
      },
      execCommand: function() {
        var rng = this.selection.getRange()
        var bk = rng.createBookmark(true)
        var cell = getTableItemsByRange(this).cell
        var ut = getUETable(cell)
        ut.splitToCells(cell)
        rng.moveToBookmark(bk).select()
      }
    }
    UE.commands['splittorows'] = {
      queryCommandState: function() {
        var tableItems = getTableItemsByRange(this)
        var cell = tableItems.cell
        if (!cell) {
          return -1
        }
        var ut = getUETable(tableItems.table)
        if (ut.selectedTds.length > 0) {
          return -1
        }
        return cell && cell.rowSpan > 1 ? 0 : -1
      },
      execCommand: function() {
        var rng = this.selection.getRange()
        var bk = rng.createBookmark(true)
        var cell = getTableItemsByRange(this).cell
        var ut = getUETable(cell)
        ut.splitToRows(cell)
        rng.moveToBookmark(bk).select()
      }
    }
    UE.commands['splittocols'] = {
      queryCommandState: function() {
        var tableItems = getTableItemsByRange(this)
        var cell = tableItems.cell
        if (!cell) {
          return -1
        }
        var ut = getUETable(tableItems.table)
        if (ut.selectedTds.length > 0) {
          return -1
        }
        return cell && cell.colSpan > 1 ? 0 : -1
      },
      execCommand: function() {
        var rng = this.selection.getRange()
        var bk = rng.createBookmark(true)
        var cell = getTableItemsByRange(this).cell
        var ut = getUETable(cell)
        ut.splitToCols(cell)
        rng.moveToBookmark(bk).select()
      }
    }
    UE.commands['adaptbytext'] = UE.commands['adaptbywindow'] = {
      queryCommandState: function() {
        return getTableItemsByRange(this).table ? 0 : -1
      },
      execCommand: function(cmd) {
        var tableItems = getTableItemsByRange(this)
        var table = tableItems.table
        if (table) {
          if (cmd == 'adaptbywindow') {
            resetTdWidth(table, this)
          } else {
            var cells = domUtils.getElementsByTagName(table, 'td th')
            utils.each(cells, function(cell) {
              cell.removeAttribute('width')
            })
            table.removeAttribute('width')
          }
        }
      }
    }
    UE.commands['averagedistributecol'] = {
      queryCommandState: function() {
        var ut = getUETableBySelected(this)
        if (!ut) {
          return -1
        }
        return ut.isFullRow() || ut.isFullCol() ? 0 : -1
      },
      execCommand: function(cmd) {
        var me = this
        var ut = getUETableBySelected(me)

        function getAverageWidth() {
          var tb = ut.table
          var averageWidth
          var sumWidth = 0
          var colsNum = 0
          var tbAttr = getDefaultValue(me, tb)
          if (ut.isFullRow()) {
            sumWidth = tb.offsetWidth
            colsNum = ut.colsNum
          } else {
            var begin = ut.cellsRange.beginColIndex
            var end = ut.cellsRange.endColIndex
            var node
            for (var i = begin; i <= end;) {
              node = ut.selectedTds[i]
              sumWidth += node.offsetWidth
              i += node.colSpan
              colsNum += 1
            }
          }
          averageWidth = Math.ceil(sumWidth / colsNum) - tbAttr.tdBorder * 2 - tbAttr.tdPadding * 2
          return averageWidth
        }

        function setAverageWidth(averageWidth) {
          utils.each(domUtils.getElementsByTagName(ut.table, 'th'), function(node) {
            node.setAttribute('width', '')
          })
          var cells = ut.isFullRow() ? domUtils.getElementsByTagName(ut.table, 'td') : ut.selectedTds
          utils.each(cells, function(node) {
            if (node.colSpan == 1) {
              node.setAttribute('width', averageWidth)
            }
          })
        }

        if (ut && ut.selectedTds.length) {
          setAverageWidth(getAverageWidth())
        }
      }
    }
    UE.commands['averagedistributerow'] = {
      queryCommandState: function() {
        var ut = getUETableBySelected(this)
        if (!ut) {
          return -1
        }
        if (ut.selectedTds && /th/gi.test(ut.selectedTds[0].tagName)) {
          return -1
        }
        return ut.isFullRow() || ut.isFullCol() ? 0 : -1
      },
      execCommand: function(cmd) {
        var me = this
        var ut = getUETableBySelected(me)

        function getAverageHeight() {
          var averageHeight
          var rowNum
          var sumHeight = 0
          var tb = ut.table
          var tbAttr = getDefaultValue(me, tb)
          var tdpadding = parseInt(domUtils.getComputedStyle(tb.getElementsByTagName('td')[0], 'padding-top'))
          if (ut.isFullCol()) {
            var captionArr = domUtils.getElementsByTagName(tb, 'caption')
            var thArr = domUtils.getElementsByTagName(tb, 'th')
            var captionHeight
            var thHeight
            if (captionArr.length > 0) {
              captionHeight = captionArr[0].offsetHeight
            }
            if (thArr.length > 0) {
              thHeight = thArr[0].offsetHeight
            }
            sumHeight = tb.offsetHeight - (captionHeight || 0) - (thHeight || 0)
            rowNum = thArr.length == 0 ? ut.rowsNum : ut.rowsNum - 1
          } else {
            var begin = ut.cellsRange.beginRowIndex
            var end = ut.cellsRange.endRowIndex
            var count = 0
            var trs = domUtils.getElementsByTagName(tb, 'tr')
            for (var i = begin; i <= end; i++) {
              sumHeight += trs[i].offsetHeight
              count += 1
            }
            rowNum = count
          }
          if (browser.ie && browser.version < 9) {
            averageHeight = Math.ceil(sumHeight / rowNum)
          } else {
            averageHeight = Math.ceil(sumHeight / rowNum) - tbAttr.tdBorder * 2 - tdpadding * 2
          }
          return averageHeight
        }

        function setAverageHeight(averageHeight) {
          var cells = ut.isFullCol() ? domUtils.getElementsByTagName(ut.table, 'td') : ut.selectedTds
          utils.each(cells, function(node) {
            if (node.rowSpan == 1) {
              node.setAttribute('height', averageHeight)
            }
          })
        }

        if (ut && ut.selectedTds.length) {
          setAverageHeight(getAverageHeight())
        }
      }
    }
    UE.commands['cellalignment'] = {
      queryCommandState: function() {
        return getTableItemsByRange(this).table ? 0 : -1
      },
      execCommand: function(cmd, data) {
        var me = this
        var ut = getUETableBySelected(me)
        if (!ut) {
          var start = me.selection.getStart()
          var cell = start && domUtils.findParentByTagName(start, ['td', 'th', 'caption'], true)
          if (!/caption/gi.test(cell.tagName)) {
            domUtils.setAttributes(cell, data)
          } else {
            cell.style.textAlign = data.align
            cell.style.verticalAlign = data.vAlign
          }
          me.selection.getRange().setCursor(true)
        } else {
          utils.each(ut.selectedTds, function(cell) {
            domUtils.setAttributes(cell, data)
          })
        }
      },
      queryCommandValue: function(cmd) {
        var activeMenuCell = getTableItemsByRange(this).cell
        if (!activeMenuCell) {
          activeMenuCell = getSelectedArr(this)[0]
        }
        if (!activeMenuCell) {
          return null
        } else {
          var cells = UE.UETable.getUETable(activeMenuCell).selectedTds
          !cells.length && (cells = activeMenuCell)
          return UE.UETable.getTableCellAlignState(cells)
        }
      }
    }
    UE.commands['tablealignment'] = {
      queryCommandState: function() {
        if (browser.ie && browser.version < 8) {
          return -1
        }
        return getTableItemsByRange(this).table ? 0 : -1
      },
      execCommand: function(cmd, value) {
        var me = this
        var start = me.selection.getStart()
        var table = start && domUtils.findParentByTagName(start, ['table'], true)
        if (table) {
          table.setAttribute('align', value)
        }
      }
    }
    UE.commands['edittable'] = {
      queryCommandState: function() {
        return getTableItemsByRange(this).table ? 0 : -1
      },
      execCommand: function(cmd, color) {
        var rng = this.selection.getRange()
        var table = domUtils.findParentByTagName(rng.startContainer, 'table')
        if (table) {
          var arr = domUtils
            .getElementsByTagName(table, 'td')
            .concat(domUtils.getElementsByTagName(table, 'th'), domUtils.getElementsByTagName(table, 'caption'))
          utils.each(arr, function(node) {
            node.style.borderColor = color
          })
        }
      }
    }
    UE.commands['edittd'] = {
      queryCommandState: function() {
        return getTableItemsByRange(this).table ? 0 : -1
      },
      execCommand: function(cmd, bkColor) {
        var me = this
        var ut = getUETableBySelected(me)
        if (!ut) {
          var start = me.selection.getStart()
          var cell = start && domUtils.findParentByTagName(start, ['td', 'th', 'caption'], true)
          if (cell) {
            cell.style.backgroundColor = bkColor
          }
        } else {
          utils.each(ut.selectedTds, function(cell) {
            cell.style.backgroundColor = bkColor
          })
        }
      }
    }
    UE.commands['settablebackground'] = {
      queryCommandState: function() {
        return getSelectedArr(this).length > 1 ? 0 : -1
      },
      execCommand: function(cmd, value) {
        var cells, ut
        cells = getSelectedArr(this)
        ut = getUETable(cells[0])
        ut.setBackground(cells, value)
      }
    }
    UE.commands['cleartablebackground'] = {
      queryCommandState: function() {
        var cells = getSelectedArr(this)
        if (!cells.length) {
          return -1
        }
        for (var i = 0, cell; (cell = cells[i++]);) {
          if (cell.style.backgroundColor !== '') {
            return 0
          }
        }
        return -1
      },
      execCommand: function() {
        var cells = getSelectedArr(this)
        var ut = getUETable(cells[0])
        ut.removeBackground(cells)
      }
    }
    UE.commands['interlacetable'] = UE.commands['uninterlacetable'] = {
      queryCommandState: function(cmd) {
        var table = getTableItemsByRange(this).table
        if (!table) {
          return -1
        }
        var interlaced = table.getAttribute('interlaced')
        if (cmd == 'interlacetable') {
          return interlaced === 'enabled' ? -1 : 0
        } else {
          return !interlaced || interlaced === 'disabled' ? -1 : 0
        }
      },
      execCommand: function(cmd, classList) {
        var table = getTableItemsByRange(this).table
        if (cmd == 'interlacetable') {
          table.setAttribute('interlaced', 'enabled')
          this.fireEvent('interlacetable', table, classList)
        } else {
          table.setAttribute('interlaced', 'disabled')
          this.fireEvent('uninterlacetable', table)
        }
      }
    }
    UE.commands['setbordervisible'] = {
      queryCommandState: function(cmd) {
        var table = getTableItemsByRange(this).table
        if (!table) {
          return -1
        }
        return 0
      },
      execCommand: function() {
        var table = getTableItemsByRange(this).table
        utils.each(domUtils.getElementsByTagName(table, 'td'), function(td) {
          td.style.borderWidth = '1px'
          td.style.borderStyle = 'solid'
        })
      }
    }

    function resetTdWidth(table, editor) {
      var tds = domUtils.getElementsByTagName(table, 'td th')
      utils.each(tds, function(td) {
        td.removeAttribute('width')
      })
      table.setAttribute('width', getTableWidth(editor, true, getDefaultValue(editor, table)))
      var tdsWidths = []
      setTimeout(function() {
        utils.each(tds, function(td) {
          td.colSpan == 1 && tdsWidths.push(td.offsetWidth)
        })
        utils.each(tds, function(td, i) {
          td.colSpan == 1 && td.setAttribute('width', tdsWidths[i] + '')
        })
      }, 0)
    }

    function getTableWidth(editor, needIEHack, defaultValue) {
      var body = editor.body
      return (
        body.offsetWidth -
        (needIEHack ? parseInt(domUtils.getComputedStyle(body, 'margin-left'), 10) * 2 : 0) -
        defaultValue.tableBorder * 2 -
        (editor.options.offsetWidth || 0)
      )
    }

    function getSelectedArr(editor) {
      var cell = getTableItemsByRange(editor).cell
      if (cell) {
        var ut = getUETable(cell)
        return ut.selectedTds.length ? ut.selectedTds : [cell]
      } else {
        return []
      }
    }
  })()
  UE.plugins['table'] = function() {
    var me = this
    var tabTimer = null
    var tableDragTimer = null
    var tableResizeTimer = null
    var cellMinWidth = 5
    var isInResizeBuffer = false
    var cellBorderWidth = 5
    var offsetOfTableCell = 10
    var singleClickState = 0
    var userActionStatus = null
    var dblclickTime = 360
    var UT = UE.UETable
    var getUETable = function(tdOrTable) {
      return UT.getUETable(tdOrTable)
    }
    var getUETableBySelected = function(editor) {
      return UT.getUETableBySelected(editor)
    }
    var getDefaultValue = function(editor, table) {
      return UT.getDefaultValue(editor, table)
    }
    var removeSelectedClass = function(cells) {
      return UT.removeSelectedClass(cells)
    }

    function showError(e) {
    }

    me.ready(function() {
      var me = this
      var orgGetText = me.selection.getText
      me.selection.getText = function() {
        var table = getUETableBySelected(me)
        if (table) {
          var str = ''
          utils.each(table.selectedTds, function(td) {
            str += td[browser.ie ? 'innerText' : 'textContent']
          })
          return str
        } else {
          return orgGetText.call(me.selection)
        }
      }
    })
    var startTd = null
    var currentTd = null
    var onDrag = ''
    var onBorder = false
    var dragButton = null
    var dragOver = false
    var dragLine = null
    var dragTd = null
    var mousedown = false
    var needIEHack = true
    me.setOpt({
      maxColNum: 20,
      maxRowNum: 100,
      defaultCols: 5,
      defaultRows: 5,
      tdvalign: 'top',
      cursorpath: me.options.UEDITOR_HOME_URL + 'themes/default/images/cursor_',
      tableDragable: false,
      classList: ['ue-table-interlace-color-single', 'ue-table-interlace-color-double']
    })
    me.getUETable = getUETable
    var commands = {
      deletetable: 1,
      inserttable: 1,
      cellvalign: 1,
      insertcaption: 1,
      deletecaption: 1,
      inserttitle: 1,
      deletetitle: 1,
      mergeright: 1,
      mergedown: 1,
      mergecells: 1,
      insertrow: 1,
      insertrownext: 1,
      deleterow: 1,
      insertcol: 1,
      insertcolnext: 1,
      deletecol: 1,
      splittocells: 1,
      splittorows: 1,
      splittocols: 1,
      adaptbytext: 1,
      adaptbywindow: 1,
      adaptbycustomer: 1,
      insertparagraph: 1,
      insertparagraphbeforetable: 1,
      averagedistributecol: 1,
      averagedistributerow: 1
    }
    me.ready(function() {
      utils.cssRule(
        'table',
        '.selectTdClass{background-color:#edf5fa !important}' +
        'table.noBorderTable td,table.noBorderTable th,table.noBorderTable caption{border:1px dashed #ddd !important}' +
        'table{margin-bottom:10px;border-collapse:collapse;display:table;}' +
        'td,th{padding: 5px 10px;border: 1px solid #DDD;}' +
        'caption{border:1px dashed #DDD;border-bottom:0;padding:3px;text-align:center;}' +
        'th{border-top:1px solid #BBB;background-color:#F7F7F7;}' +
        'table tr.firstRow th{border-top-width:2px;}' +
        '.ue-table-interlace-color-single{ background-color: #fcfcfc; } .ue-table-interlace-color-double{ background-color: #f7faff; }' +
        'td p{margin:0;padding:0;}',
        me.document
      )
      var tableCopyList, isFullCol, isFullRow
      me.addListener('keydown', function(cmd, evt) {
        var me = this
        var keyCode = evt.keyCode || evt.which
        if (keyCode == 8) {
          var ut = getUETableBySelected(me)
          if (ut && ut.selectedTds.length) {
            if (ut.isFullCol()) {
              me.execCommand('deletecol')
            } else {
              if (ut.isFullRow()) {
                me.execCommand('deleterow')
              } else {
                me.fireEvent('delcells')
              }
            }
            domUtils.preventDefault(evt)
          }
          var caption = domUtils.findParentByTagName(me.selection.getStart(), 'caption', true)
          var range = me.selection.getRange()
          if (range.collapsed && caption && isEmptyBlock(caption)) {
            me.fireEvent('saveScene')
            var table = caption.parentNode
            domUtils.remove(caption)
            if (table) {
              range.setStart(table.rows[0].cells[0], 0).setCursor(false, true)
            }
            me.fireEvent('saveScene')
          }
        }
        if (keyCode == 46) {
          ut = getUETableBySelected(me)
          if (ut) {
            me.fireEvent('saveScene')
            for (var i = 0, ci; (ci = ut.selectedTds[i++]);) {
              domUtils.fillNode(me.document, ci)
            }
            me.fireEvent('saveScene')
            domUtils.preventDefault(evt)
          }
        }
        if (keyCode == 13) {
          var rng = me.selection.getRange()
          var caption = domUtils.findParentByTagName(rng.startContainer, 'caption', true)
          if (caption) {
            var table = domUtils.findParentByTagName(caption, 'table')
            if (!rng.collapsed) {
              rng.deleteContents()
              me.fireEvent('saveScene')
            } else {
              if (caption) {
                rng.setStart(table.rows[0].cells[0], 0).setCursor(false, true)
              }
            }
            domUtils.preventDefault(evt)
            return
          }
          if (rng.collapsed) {
            var table = domUtils.findParentByTagName(rng.startContainer, 'table')
            if (table) {
              var cell = table.rows[0].cells[0]
              var start = domUtils.findParentByTagName(me.selection.getStart(), ['td', 'th'], true)
              var preNode = table.previousSibling
              if (
                cell === start &&
                (!preNode || (preNode.nodeType == 1 && preNode.tagName == 'TABLE')) &&
                domUtils.isStartInblock(rng)
              ) {
                var first = domUtils.findParent(
                  me.selection.getStart(),
                  function(n) {
                    return domUtils.isBlockElm(n)
                  },
                  true
                )
                if (first && (/t(h|d)/i.test(first.tagName) || first === start.firstChild)) {
                  me.execCommand('insertparagraphbeforetable')
                  domUtils.preventDefault(evt)
                }
              }
            }
          }
        }
        if ((evt.ctrlKey || evt.metaKey) && evt.keyCode == '67') {
          tableCopyList = null
          var ut = getUETableBySelected(me)
          if (ut) {
            var tds = ut.selectedTds
            isFullCol = ut.isFullCol()
            isFullRow = ut.isFullRow()
            tableCopyList = [[ut.cloneCell(tds[0], null, true)]]
            for (var i = 1, ci; (ci = tds[i]); i++) {
              if (ci.parentNode !== tds[i - 1].parentNode) {
                tableCopyList.push([ut.cloneCell(ci, null, true)])
              } else {
                tableCopyList[tableCopyList.length - 1].push(ut.cloneCell(ci, null, true))
              }
            }
          }
        }
      })
      me.addListener('tablehasdeleted', function() {
        toggleDraggableState(this, false, '', null)
        if (dragButton) {
          domUtils.remove(dragButton)
        }
      })
      me.addListener('beforepaste', function(cmd, html) {
        var me = this
        var rng = me.selection.getRange()
        if (domUtils.findParentByTagName(rng.startContainer, 'caption', true)) {
          var div = me.document.createElement('div')
          div.innerHTML = html.html
          html.html = div[browser.ie9below ? 'innerText' : 'textContent']
          return
        }
        var table = getUETableBySelected(me)
        if (tableCopyList) {
          me.fireEvent('saveScene')
          var rng = me.selection.getRange()
          var td = domUtils.findParentByTagName(rng.startContainer, ['td', 'th'], true)
          var tmpNode
          var preNode
          if (td) {
            var ut = getUETable(td)
            if (isFullRow) {
              var rowIndex = ut.getCellInfo(td).rowIndex
              if (td.tagName == 'TH') {
                rowIndex++
              }
              for (var i = 0, ci; (ci = tableCopyList[i++]);) {
                var tr = ut.insertRow(rowIndex++, 'td')
                for (var j = 0, cj; (cj = ci[j]); j++) {
                  var cell = tr.cells[j]
                  if (!cell) {
                    cell = tr.insertCell(j)
                  }
                  cell.innerHTML = cj.innerHTML
                  cj.getAttribute('width') && cell.setAttribute('width', cj.getAttribute('width'))
                  cj.getAttribute('vAlign') && cell.setAttribute('vAlign', cj.getAttribute('vAlign'))
                  cj.getAttribute('align') && cell.setAttribute('align', cj.getAttribute('align'))
                  cj.style.cssText && (cell.style.cssText = cj.style.cssText)
                }
                for (var j = 0, cj; (cj = tr.cells[j]); j++) {
                  if (!ci[j]) {
                    break
                  }
                  cj.innerHTML = ci[j].innerHTML
                  ci[j].getAttribute('width') && cj.setAttribute('width', ci[j].getAttribute('width'))
                  ci[j].getAttribute('vAlign') && cj.setAttribute('vAlign', ci[j].getAttribute('vAlign'))
                  ci[j].getAttribute('align') && cj.setAttribute('align', ci[j].getAttribute('align'))
                  ci[j].style.cssText && (cj.style.cssText = ci[j].style.cssText)
                }
              }
            } else {
              if (isFullCol) {
                cellInfo = ut.getCellInfo(td)
                var maxColNum = 0
                for (var j = 0, ci = tableCopyList[0], cj; (cj = ci[j++]);) {
                  maxColNum += cj.colSpan || 1
                }
                me.__hasEnterExecCommand = true
                for (i = 0; i < maxColNum; i++) {
                  me.execCommand('insertcol')
                }
                me.__hasEnterExecCommand = false
                td = ut.table.rows[0].cells[cellInfo.cellIndex]
                if (td.tagName == 'TH') {
                  td = ut.table.rows[1].cells[cellInfo.cellIndex]
                }
              }
              for (var i = 0, ci; (ci = tableCopyList[i++]);) {
                tmpNode = td
                for (var j = 0, cj; (cj = ci[j++]);) {
                  if (td) {
                    td.innerHTML = cj.innerHTML
                    cj.getAttribute('width') && td.setAttribute('width', cj.getAttribute('width'))
                    cj.getAttribute('vAlign') && td.setAttribute('vAlign', cj.getAttribute('vAlign'))
                    cj.getAttribute('align') && td.setAttribute('align', cj.getAttribute('align'))
                    cj.style.cssText && (td.style.cssText = cj.style.cssText)
                    preNode = td
                    td = td.nextSibling
                  } else {
                    var cloneTd = cj.cloneNode(true)
                    domUtils.removeAttributes(cloneTd, ['class', 'rowSpan', 'colSpan'])
                    preNode.parentNode.appendChild(cloneTd)
                  }
                }
                td = ut.getNextCell(tmpNode, true, true)
                if (!tableCopyList[i]) {
                  break
                }
                if (!td) {
                  var cellInfo = ut.getCellInfo(tmpNode)
                  ut.table.insertRow(ut.table.rows.length)
                  ut.update()
                  td = ut.getVSideCell(tmpNode, true)
                }
              }
            }
            ut.update()
          } else {
            table = me.document.createElement('table')
            for (var i = 0, ci; (ci = tableCopyList[i++]);) {
              var tr = table.insertRow(table.rows.length)
              for (var j = 0, cj; (cj = ci[j++]);) {
                cloneTd = UT.cloneCell(cj, null, true)
                domUtils.removeAttributes(cloneTd, ['class'])
                tr.appendChild(cloneTd)
              }
              if (j == 2 && cloneTd.rowSpan > 1) {
                cloneTd.rowSpan = 1
              }
            }
            var defaultValue = getDefaultValue(me)
            var width =
              me.body.offsetWidth -
              (needIEHack ? parseInt(domUtils.getComputedStyle(me.body, 'margin-left'), 10) * 2 : 0) -
              defaultValue.tableBorder * 2 -
              (me.options.offsetWidth || 0)
            me.execCommand(
              'insertHTML',
              '<table  ' +
              (isFullCol && isFullRow ? 'width="' + width + '"' : '') +
              '>' +
              table.innerHTML.replace(/>\s*</g, '><').replace(/\bth\b/gi, 'td') +
              '</table>'
            )
          }
          me.fireEvent('contentchange')
          me.fireEvent('saveScene')
          html.html = ''
          return true
        } else {
          var div = me.document.createElement('div')
          var tables
          div.innerHTML = html.html
          tables = div.getElementsByTagName('table')
          if (domUtils.findParentByTagName(me.selection.getStart(), 'table')) {
            utils.each(tables, function(t) {
              domUtils.remove(t)
            })
            if (domUtils.findParentByTagName(me.selection.getStart(), 'caption', true)) {
              div.innerHTML = div[browser.ie ? 'innerText' : 'textContent']
            }
          } else {
            utils.each(tables, function(table) {
              removeStyleSize(table, true)
              domUtils.removeAttributes(table, ['style', 'border'])
              utils.each(domUtils.getElementsByTagName(table, 'td'), function(td) {
                if (isEmptyBlock(td)) {
                  domUtils.fillNode(me.document, td)
                }
                removeStyleSize(td, true)
              })
            })
          }
          html.html = div.innerHTML
        }
      })
      me.addListener('afterpaste', function() {
        utils.each(domUtils.getElementsByTagName(me.body, 'table'), function(table) {
          if (table.offsetWidth > me.body.offsetWidth) {
            var defaultValue = getDefaultValue(me, table)
            table.style.width =
              me.body.offsetWidth -
              (needIEHack ? parseInt(domUtils.getComputedStyle(me.body, 'margin-left'), 10) * 2 : 0) -
              defaultValue.tableBorder * 2 -
              (me.options.offsetWidth || 0) +
              'px'
          }
        })
      })
      me.addListener('blur', function() {
        tableCopyList = null
      })
      var timer
      me.addListener('keydown', function() {
        clearTimeout(timer)
        timer = setTimeout(function() {
          var rng = me.selection.getRange()
          var cell = domUtils.findParentByTagName(rng.startContainer, ['th', 'td'], true)
          if (cell) {
            var table = cell.parentNode.parentNode.parentNode
            if (table.offsetWidth > table.getAttribute('width')) {
              cell.style.wordBreak = 'break-all'
            }
          }
        }, 100)
      })
      me.addListener('selectionchange', function() {
        toggleDraggableState(me, false, '', null)
      })
      me.addListener('contentchange', function() {
        var me = this
        hideDragLine(me)
        if (getUETableBySelected(me)) {
          return
        }
        var rng = me.selection.getRange()
        var start = rng.startContainer
        start = domUtils.findParentByTagName(start, ['td', 'th'], true)
        utils.each(domUtils.getElementsByTagName(me.document, 'table'), function(table) {
          if (me.fireEvent('excludetable', table) === true) {
            return
          }
          table.ueTable = new UT(table)
          table.onmouseover = function() {
            me.fireEvent('tablemouseover', table)
          }
          table.onmousemove = function() {
            me.fireEvent('tablemousemove', table)
            me.options.tableDragable && toggleDragButton(true, this, me)
            utils.defer(function() {
              me.fireEvent('contentchange', 50)
            }, true)
          }
          table.onmouseout = function() {
            me.fireEvent('tablemouseout', table)
            toggleDraggableState(me, false, '', null)
            hideDragLine(me)
          }
          table.onclick = function(evt) {
            evt = me.window.event || evt
            var target = getParentTdOrTh(evt.target || evt.srcElement)
            if (!target) {
              return
            }
            var ut = getUETable(target)
            var table = ut.table
            var cellInfo = ut.getCellInfo(target)
            var cellsRange
            var rng = me.selection.getRange()
            if (inTableSide(table, target, evt, true)) {
              var endTdCol = ut.getCell(
                ut.indexTable[ut.rowsNum - 1][cellInfo.colIndex].rowIndex,
                ut.indexTable[ut.rowsNum - 1][cellInfo.colIndex].cellIndex
              )
              if (evt.shiftKey && ut.selectedTds.length) {
                if (ut.selectedTds[0] !== endTdCol) {
                  cellsRange = ut.getCellsRange(ut.selectedTds[0], endTdCol)
                  ut.setSelected(cellsRange)
                } else {
                  rng && rng.selectNodeContents(endTdCol).select()
                }
              } else {
                if (target !== endTdCol) {
                  cellsRange = ut.getCellsRange(target, endTdCol)
                  ut.setSelected(cellsRange)
                } else {
                  rng && rng.selectNodeContents(endTdCol).select()
                }
              }
              return
            }
            if (inTableSide(table, target, evt)) {
              var endTdRow = ut.getCell(
                ut.indexTable[cellInfo.rowIndex][ut.colsNum - 1].rowIndex,
                ut.indexTable[cellInfo.rowIndex][ut.colsNum - 1].cellIndex
              )
              if (evt.shiftKey && ut.selectedTds.length) {
                if (ut.selectedTds[0] !== endTdRow) {
                  cellsRange = ut.getCellsRange(ut.selectedTds[0], endTdRow)
                  ut.setSelected(cellsRange)
                } else {
                  rng && rng.selectNodeContents(endTdRow).select()
                }
              } else {
                if (target !== endTdRow) {
                  cellsRange = ut.getCellsRange(target, endTdRow)
                  ut.setSelected(cellsRange)
                } else {
                  rng && rng.selectNodeContents(endTdRow).select()
                }
              }
            }
          }
        })
        switchBorderColor(me, true)
      })
      domUtils.on(me.document, 'mousemove', mouseMoveEvent)
      domUtils.on(me.document, 'mouseout', function(evt) {
        var target = evt.target || evt.srcElement
        if (target.tagName == 'TABLE') {
          toggleDraggableState(me, false, '', null)
        }
      })
      me.addListener('interlacetable', function(type, table, classList) {
        if (!table) {
          return
        }
        var me = this
        var rows = table.rows
        var len = rows.length
        var getClass = function(list, index, repeat) {
          return list[index] ? list[index] : repeat ? list[index % list.length] : ''
        }
        for (var i = 0; i < len; i++) {
          rows[i].className = getClass(classList || me.options.classList, i, true)
        }
      })
      me.addListener('uninterlacetable', function(type, table) {
        if (!table) {
          return
        }
        var me = this
        var rows = table.rows
        var classList = me.options.classList
        var len = rows.length
        for (var i = 0; i < len; i++) {
          domUtils.removeClasses(rows[i], classList)
        }
      })
      me.addListener('mousedown', mouseDownEvent)
      me.addListener('mouseup', mouseUpEvent)
      domUtils.on(me.body, 'dragstart', function(evt) {
        mouseUpEvent.call(me, 'dragstart', evt)
      })
      me.addOutputRule(function(root) {
        utils.each(root.getNodesByTagName('div'), function(n) {
          if (n.getAttr('id') == 'ue_tableDragLine') {
            n.parentNode.removeChild(n)
          }
        })
      })
      var currentRowIndex = 0
      me.addListener('mousedown', function() {
        currentRowIndex = 0
      })
      me.addListener('tabkeydown', function() {
        var range = this.selection.getRange()
        var common = range.getCommonAncestor(true, true)
        var table = domUtils.findParentByTagName(common, 'table')
        if (table) {
          if (domUtils.findParentByTagName(common, 'caption', true)) {
            var cell = domUtils.getElementsByTagName(table, 'th td')
            if (cell && cell.length) {
              range.setStart(cell[0], 0).setCursor(false, true)
            }
          } else {
            var cell = domUtils.findParentByTagName(common, ['td', 'th'], true)
            var ua = getUETable(cell)
            currentRowIndex = cell.rowSpan > 1 ? currentRowIndex : ua.getCellInfo(cell).rowIndex
            var nextCell = ua.getTabNextCell(cell, currentRowIndex)
            if (nextCell) {
              if (isEmptyBlock(nextCell)) {
                range.setStart(nextCell, 0).setCursor(false, true)
              } else {
                range.selectNodeContents(nextCell).select()
              }
            } else {
              me.fireEvent('saveScene')
              me.__hasEnterExecCommand = true
              this.execCommand('insertrownext')
              me.__hasEnterExecCommand = false
              range = this.selection.getRange()
              range.setStart(table.rows[table.rows.length - 1].cells[0], 0).setCursor()
              me.fireEvent('saveScene')
            }
          }
          return true
        }
      })
      browser.ie &&
      me.addListener('selectionchange', function() {
        toggleDraggableState(this, false, '', null)
      })
      me.addListener('keydown', function(type, evt) {
        var me = this
        var keyCode = evt.keyCode || evt.which
        if (keyCode == 8 || keyCode == 46) {
          return
        }
        var notCtrlKey = !evt.ctrlKey && !evt.metaKey && !evt.shiftKey && !evt.altKey
        notCtrlKey && removeSelectedClass(domUtils.getElementsByTagName(me.body, 'td'))
        var ut = getUETableBySelected(me)
        if (!ut) {
          return
        }
        notCtrlKey && ut.clearSelected()
      })
      me.addListener('beforegetcontent', function() {
        switchBorderColor(this, false)
        browser.ie &&
        utils.each(this.document.getElementsByTagName('caption'), function(ci) {
          if (domUtils.isEmptyNode(ci)) {
            ci.innerHTML = '&nbsp;'
          }
        })
      })
      me.addListener('aftergetcontent', function() {
        switchBorderColor(this, true)
      })
      me.addListener('getAllHtml', function() {
        removeSelectedClass(me.document.getElementsByTagName('td'))
      })
      me.addListener('fullscreenchanged', function(type, fullscreen) {
        if (!fullscreen) {
          var ratio = this.body.offsetWidth / document.body.offsetWidth
          var tables = domUtils.getElementsByTagName(this.body, 'table')
          utils.each(tables, function(table) {
            if (table.offsetWidth < me.body.offsetWidth) {
              return false
            }
            var tds = domUtils.getElementsByTagName(table, 'td')
            var backWidths = []
            utils.each(tds, function(td) {
              backWidths.push(td.offsetWidth)
            })
            for (var i = 0, td; (td = tds[i]); i++) {
              td.setAttribute('width', Math.floor(backWidths[i] * ratio))
            }
            table.setAttribute('width', Math.floor(getTableWidth(me, needIEHack, getDefaultValue(me))))
          })
        }
      })
      var oldExecCommand = me.execCommand
      me.execCommand = function(cmd, datatat) {
        var me = this
        var args = arguments
        cmd = cmd.toLowerCase()
        var ut = getUETableBySelected(me)
        var tds
        var range = new dom.Range(me.document)
        var cmdFun = me.commands[cmd] || UE.commands[cmd]
        var result
        if (!cmdFun) {
          return
        }
        if (ut && !commands[cmd] && !cmdFun.notNeedUndo && !me.__hasEnterExecCommand) {
          me.__hasEnterExecCommand = true
          me.fireEvent('beforeexeccommand', cmd)
          tds = ut.selectedTds
          var lastState = -2
          var lastValue = -2
          var value
          var state
          for (var i = 0, td; (td = tds[i]); i++) {
            if (isEmptyBlock(td)) {
              range.setStart(td, 0).setCursor(false, true)
            } else {
              range.selectNode(td).select(true)
            }
            state = me.queryCommandState(cmd)
            value = me.queryCommandValue(cmd)
            if (state != -1) {
              if (lastState !== state || lastValue !== value) {
                me._ignoreContentChange = true
                result = oldExecCommand.apply(me, arguments)
                me._ignoreContentChange = false
              }
              lastState = me.queryCommandState(cmd)
              lastValue = me.queryCommandValue(cmd)
              if (domUtils.isEmptyBlock(td)) {
                domUtils.fillNode(me.document, td)
              }
            }
          }
          range
            .setStart(tds[0], 0)
            .shrinkBoundary(true)
            .setCursor(false, true)
          me.fireEvent('contentchange')
          me.fireEvent('afterexeccommand', cmd)
          me.__hasEnterExecCommand = false
          me._selectionChange()
        } else {
          result = oldExecCommand.apply(me, arguments)
        }
        return result
      }
    })

    function removeStyleSize(obj, replaceToProperty) {
      removeStyle(obj, 'width', true)
      removeStyle(obj, 'height', true)
    }

    function removeStyle(obj, styleName, replaceToProperty) {
      if (obj.style[styleName]) {
        replaceToProperty && obj.setAttribute(styleName, parseInt(obj.style[styleName], 10))
        obj.style[styleName] = ''
      }
    }

    function getParentTdOrTh(ele) {
      if (ele.tagName == 'TD' || ele.tagName == 'TH') {
        return ele
      }
      var td
      if ((td = domUtils.findParentByTagName(ele, 'td', true) || domUtils.findParentByTagName(ele, 'th', true))) {
        return td
      }
      return null
    }

    function isEmptyBlock(node) {
      var reg = new RegExp(domUtils.fillChar, 'g')
      if (node[browser.ie ? 'innerText' : 'textContent'].replace(/^\s*$/, '').replace(reg, '').length > 0) {
        return 0
      }
      for (var n in dtd.$isNotEmpty) {
        if (node.getElementsByTagName(n).length) {
          return 0
        }
      }
      return 1
    }

    function mouseCoords(evt) {
      if (evt.pageX || evt.pageY) {
        return { x: evt.pageX, y: evt.pageY }
      }
      return {
        x: evt.clientX + me.document.body.scrollLeft - me.document.body.clientLeft,
        y: evt.clientY + me.document.body.scrollTop - me.document.body.clientTop
      }
    }

    function mouseMoveEvent(evt) {
      if (isEditorDisabled()) {
        return
      }
      try {
        var target = getParentTdOrTh(evt.target || evt.srcElement)
        var pos
        if (isInResizeBuffer) {
          me.body.style.webkitUserSelect = 'none'
          if (
            Math.abs(userActionStatus.x - evt.clientX) > offsetOfTableCell ||
            Math.abs(userActionStatus.y - evt.clientY) > offsetOfTableCell
          ) {
            clearTableDragTimer()
            isInResizeBuffer = false
            singleClickState = 0
            tableBorderDrag(evt)
          }
        }
        if (onDrag && dragTd) {
          singleClickState = 0
          me.body.style.webkitUserSelect = 'none'
          me.selection.getNative()[browser.ie9below ? 'empty' : 'removeAllRanges']()
          pos = mouseCoords(evt)
          toggleDraggableState(me, true, onDrag, pos, target)
          if (onDrag == 'h') {
            dragLine.style.left = getPermissionX(dragTd, evt) + 'px'
          } else {
            if (onDrag == 'v') {
              dragLine.style.top = getPermissionY(dragTd, evt) + 'px'
            }
          }
          return
        }
        if (target) {
          if (me.fireEvent('excludetable', target) === true) {
            return
          }
          pos = mouseCoords(evt)
          var state = getRelation(target, pos)
          var table = domUtils.findParentByTagName(target, 'table', true)
          if (inTableSide(table, target, evt, true)) {
            if (me.fireEvent('excludetable', table) === true) {
              return
            }
            me.body.style.cursor = 'url(' + me.options.cursorpath + 'h.png),pointer'
          } else {
            if (inTableSide(table, target, evt)) {
              if (me.fireEvent('excludetable', table) === true) {
                return
              }
              me.body.style.cursor = 'url(' + me.options.cursorpath + 'v.png),pointer'
            } else {
              me.body.style.cursor = 'text'
              var curCell = target
              if (/\d/.test(state)) {
                state = state.replace(/\d/, '')
                target = getUETable(target).getPreviewCell(target, state == 'v')
              }
              toggleDraggableState(me, target ? !!state : false, target ? state : '', pos, target)
            }
          }
        } else {
          toggleDragButton(false, table, me)
        }
      } catch (e) {
        showError(e)
      }
    }

    var dragButtonTimer

    function toggleDragButton(show, table, editor) {
      if (!show) {
        if (dragOver) {
          return
        }
        dragButtonTimer = setTimeout(function() {
          !dragOver && dragButton && dragButton.parentNode && dragButton.parentNode.removeChild(dragButton)
        }, 2000)
      } else {
        createDragButton(table, editor)
      }
    }

    function createDragButton(table, editor) {
      var pos = domUtils.getXY(table)
      var doc = table.ownerDocument
      if (dragButton && dragButton.parentNode) {
        return dragButton
      }
      dragButton = doc.createElement('div')
      dragButton.contentEditable = false
      dragButton.innerHTML = ''
      dragButton.style.cssText =
        'width:15px;height:15px;background-image:url(' +
        editor.options.UEDITOR_HOME_URL +
        'dialogs/table/dragicon.png);position: absolute;cursor:move;top:' +
        (pos.y - 15) +
        'px;left:' +
        pos.x +
        'px;'
      domUtils.unSelectable(dragButton)
      dragButton.onmouseover = function(evt) {
        dragOver = true
      }
      dragButton.onmouseout = function(evt) {
        dragOver = false
      }
      domUtils.on(dragButton, 'click', function(type, evt) {
        doClick(evt, this)
      })
      domUtils.on(dragButton, 'dblclick', function(type, evt) {
        doDblClick(evt)
      })
      domUtils.on(dragButton, 'dragstart', function(type, evt) {
        domUtils.preventDefault(evt)
      })
      var timer

      function doClick(evt, button) {
        clearTimeout(timer)
        timer = setTimeout(function() {
          editor.fireEvent('tableClicked', table, button)
        }, 300)
      }

      function doDblClick(evt) {
        clearTimeout(timer)
        var ut = getUETable(table)
        var start = table.rows[0].cells[0]
        var end = ut.getLastCell()
        var range = ut.getCellsRange(start, end)
        editor.selection
          .getRange()
          .setStart(start, 0)
          .setCursor(false, true)
        ut.setSelected(range)
      }

      doc.body.appendChild(dragButton)
    }

    function inTableSide(table, cell, evt, top) {
      var pos = mouseCoords(evt)
      var state = getRelation(cell, pos)
      if (top) {
        var caption = table.getElementsByTagName('caption')[0]
        var capHeight = caption ? caption.offsetHeight : 0
        return state == 'v1' && pos.y - domUtils.getXY(table).y - capHeight < 8
      } else {
        return state == 'h1' && pos.x - domUtils.getXY(table).x < 8
      }
    }

    function getPermissionX(dragTd, evt) {
      var ut = getUETable(dragTd)
      if (ut) {
        var preTd = ut.getSameEndPosCells(dragTd, 'x')[0]
        var nextTd = ut.getSameStartPosXCells(dragTd)[0]
        var mouseX = mouseCoords(evt).x
        var left = (preTd ? domUtils.getXY(preTd).x : domUtils.getXY(ut.table).x) + 20
        var right = nextTd
          ? domUtils.getXY(nextTd).x + nextTd.offsetWidth - 20
          : me.body.offsetWidth + 5 || parseInt(domUtils.getComputedStyle(me.body, 'width'), 10)
        left += cellMinWidth
        right -= cellMinWidth
        return mouseX < left ? left : mouseX > right ? right : mouseX
      }
    }

    function getPermissionY(dragTd, evt) {
      try {
        var top = domUtils.getXY(dragTd).y
        var mousePosY = mouseCoords(evt).y
        return mousePosY < top ? top : mousePosY
      } catch (e) {
        showError(e)
      }
    }

    function toggleDraggableState(editor, draggable, dir, mousePos, cell) {
      try {
        editor.body.style.cursor = dir == 'h' ? 'col-resize' : dir == 'v' ? 'row-resize' : 'text'
        if (browser.ie) {
          if (dir && !mousedown && !getUETableBySelected(editor)) {
            getDragLine(editor, editor.document)
            showDragLineAt(dir, cell)
          } else {
            hideDragLine(editor)
          }
        }
        onBorder = draggable
      } catch (e) {
        showError(e)
      }
    }

    function getResizeLineByUETable() {
      var lineId = '_UETableResizeLine'
      var line = this.document.getElementById(lineId)
      if (!line) {
        line = this.document.createElement('div')
        line.id = lineId
        line.contnetEditable = false
        line.setAttribute('unselectable', 'on')
        var styles = {
          width: 2 * cellBorderWidth + 1 + 'px',
          position: 'absolute',
          'z-index': 100000,
          cursor: 'col-resize',
          background: 'red',
          display: 'none'
        }
        line.onmouseout = function() {
          this.style.display = 'none'
        }
        utils.extend(line.style, styles)
        this.document.body.appendChild(line)
      }
      return line
    }

    function updateResizeLine(cell, uetable) {
      var line = getResizeLineByUETable.call(this)
      var table = uetable.table
      var styles = {
        top: domUtils.getXY(table).y + 'px',
        left: domUtils.getXY(cell).x + cell.offsetWidth - cellBorderWidth + 'px',
        display: 'block',
        height: table.offsetHeight + 'px'
      }
      utils.extend(line.style, styles)
    }

    function showResizeLine(cell) {
      var uetable = getUETable(cell)
      updateResizeLine.call(this, cell, uetable)
    }

    function getRelation(ele, mousePos) {
      var elePos = domUtils.getXY(ele)
      if (!elePos) {
        return ''
      }
      if (elePos.x + ele.offsetWidth - mousePos.x < cellBorderWidth) {
        return 'h'
      }
      if (mousePos.x - elePos.x < cellBorderWidth) {
        return 'h1'
      }
      if (elePos.y + ele.offsetHeight - mousePos.y < cellBorderWidth) {
        return 'v'
      }
      if (mousePos.y - elePos.y < cellBorderWidth) {
        return 'v1'
      }
      return ''
    }

    function mouseDownEvent(type, evt) {
      if (isEditorDisabled()) {
        return
      }
      userActionStatus = { x: evt.clientX, y: evt.clientY }
      if (evt.button == 2) {
        var ut = getUETableBySelected(me)
        var flag = false
        if (ut) {
          var td = getTargetTd(me, evt)
          utils.each(ut.selectedTds, function(ti) {
            if (ti === td) {
              flag = true
            }
          })
          if (!flag) {
            removeSelectedClass(domUtils.getElementsByTagName(me.body, 'th td'))
            ut.clearSelected()
          } else {
            td = ut.selectedTds[0]
            setTimeout(function() {
              me.selection
                .getRange()
                .setStart(td, 0)
                .setCursor(false, true)
            }, 0)
          }
        }
      } else {
        tableClickHander(evt)
      }
    }

    function clearTableTimer() {
      tabTimer && clearTimeout(tabTimer)
      tabTimer = null
    }

    function tableDbclickHandler(evt) {
      singleClickState = 0
      evt = evt || me.window.event
      var target = getParentTdOrTh(evt.target || evt.srcElement)
      if (target) {
        var h
        if ((h = getRelation(target, mouseCoords(evt)))) {
          hideDragLine(me)
          if (h == 'h1') {
            h = 'h'
            if (inTableSide(domUtils.findParentByTagName(target, 'table'), target, evt)) {
              me.execCommand('adaptbywindow')
            } else {
              target = getUETable(target).getPreviewCell(target)
              if (target) {
                var rng = me.selection.getRange()
                rng.selectNodeContents(target).setCursor(true, true)
              }
            }
          }
          if (h == 'h') {
            var ut = getUETable(target)
            var table = ut.table
            var cells = getCellsByMoveBorder(target, table, true)
            cells = extractArray(cells, 'left')
            ut.width = ut.offsetWidth
            var oldWidth = []
            var newWidth = []
            utils.each(cells, function(cell) {
              oldWidth.push(cell.offsetWidth)
            })
            utils.each(cells, function(cell) {
              cell.removeAttribute('width')
            })
            window.setTimeout(function() {
              var changeable = true
              utils.each(cells, function(cell, index) {
                var width = cell.offsetWidth
                if (width > oldWidth[index]) {
                  changeable = false
                  return false
                }
                newWidth.push(width)
              })
              var change = changeable ? newWidth : oldWidth
              utils.each(cells, function(cell, index) {
                cell.width = change[index] - getTabcellSpace()
              })
            }, 0)
          }
        }
      }
    }

    function tableClickHander(evt) {
      removeSelectedClass(domUtils.getElementsByTagName(me.body, 'td th'))
      utils.each(me.document.getElementsByTagName('table'), function(t) {
        t.ueTable = null
      })
      startTd = getTargetTd(me, evt)
      if (!startTd) {
        return
      }
      var table = domUtils.findParentByTagName(startTd, 'table', true)
      ut = getUETable(table)
      ut && ut.clearSelected()
      if (!onBorder) {
        me.document.body.style.webkitUserSelect = ''
        mousedown = true
        me.addListener('mouseover', mouseOverEvent)
      } else {
        borderActionHandler(evt)
      }
    }

    function borderActionHandler(evt) {
      if (browser.ie) {
        evt = reconstruct(evt)
      }
      clearTableDragTimer()
      isInResizeBuffer = true
      tableDragTimer = setTimeout(function() {
        tableBorderDrag(evt)
      }, dblclickTime)
    }

    function extractArray(originArr, key) {
      var result = []
      var tmp = null
      for (var i = 0, len = originArr.length; i < len; i++) {
        tmp = originArr[i][key]
        if (tmp) {
          result.push(tmp)
        }
      }
      return result
    }

    function clearTableDragTimer() {
      tableDragTimer && clearTimeout(tableDragTimer)
      tableDragTimer = null
    }

    function reconstruct(obj) {
      var attrs = ['pageX', 'pageY', 'clientX', 'clientY', 'srcElement', 'target']
      var newObj = {}
      if (obj) {
        for (var i = 0, key, val; (key = attrs[i]); i++) {
          val = obj[key]
          val && (newObj[key] = val)
        }
      }
      return newObj
    }

    function tableBorderDrag(evt) {
      isInResizeBuffer = false
      startTd = evt.target || evt.srcElement
      if (!startTd) {
        return
      }
      var state = getRelation(startTd, mouseCoords(evt))
      if (/\d/.test(state)) {
        state = state.replace(/\d/, '')
        startTd = getUETable(startTd).getPreviewCell(startTd, state == 'v')
      }
      hideDragLine(me)
      getDragLine(me, me.document)
      me.fireEvent('saveScene')
      showDragLineAt(state, startTd)
      mousedown = true
      onDrag = state
      dragTd = startTd
    }

    function mouseUpEvent(type, evt) {
      if (isEditorDisabled()) {
        return
      }
      clearTableDragTimer()
      isInResizeBuffer = false
      if (onBorder) {
        singleClickState = ++singleClickState % 3
        userActionStatus = { x: evt.clientX, y: evt.clientY }
        tableResizeTimer = setTimeout(function() {
          singleClickState > 0 && singleClickState--
        }, dblclickTime)
        if (singleClickState === 2) {
          singleClickState = 0
          tableDbclickHandler(evt)
          return
        }
      }
      if (evt.button == 2) {
        return
      }
      var me = this
      var range = me.selection.getRange()
      var start = domUtils.findParentByTagName(range.startContainer, 'table', true)
      var end = domUtils.findParentByTagName(range.endContainer, 'table', true)
      if (start || end) {
        if (start === end) {
          start = domUtils.findParentByTagName(range.startContainer, ['td', 'th', 'caption'], true)
          end = domUtils.findParentByTagName(range.endContainer, ['td', 'th', 'caption'], true)
          if (start !== end) {
            me.selection.clearRange()
          }
        } else {
          me.selection.clearRange()
        }
      }
      mousedown = false
      me.document.body.style.webkitUserSelect = ''
      if (onDrag && dragTd) {
        me.selection.getNative()[browser.ie9below ? 'empty' : 'removeAllRanges']()
        singleClickState = 0
        dragLine = me.document.getElementById('ue_tableDragLine')
        if (dragLine) {
          var dragTdPos = domUtils.getXY(dragTd)
          var dragLinePos = domUtils.getXY(dragLine)
          switch (onDrag) {
            case 'h':
              changeColWidth(dragTd, dragLinePos.x - dragTdPos.x)
              break
            case 'v':
              changeRowHeight(dragTd, dragLinePos.y - dragTdPos.y - dragTd.offsetHeight)
              break
            default:
          }
          onDrag = ''
          dragTd = null
          hideDragLine(me)
          me.fireEvent('saveScene')
          return
        }
      }
      if (!startTd) {
        var target = domUtils.findParentByTagName(evt.target || evt.srcElement, 'td', true)
        if (!target) {
          target = domUtils.findParentByTagName(evt.target || evt.srcElement, 'th', true)
        }
        if (target && (target.tagName == 'TD' || target.tagName == 'TH')) {
          if (me.fireEvent('excludetable', target) === true) {
            return
          }
          range = new dom.Range(me.document)
          range.setStart(target, 0).setCursor(false, true)
        }
      } else {
        var ut = getUETable(startTd)
        var cell = ut ? ut.selectedTds[0] : null
        if (cell) {
          range = new dom.Range(me.document)
          if (domUtils.isEmptyBlock(cell)) {
            range.setStart(cell, 0).setCursor(false, true)
          } else {
            range
              .selectNodeContents(cell)
              .shrinkBoundary()
              .setCursor(false, true)
          }
        } else {
          range = me.selection.getRange().shrinkBoundary()
          if (!range.collapsed) {
            var start = domUtils.findParentByTagName(range.startContainer, ['td', 'th'], true)
            var end = domUtils.findParentByTagName(range.endContainer, ['td', 'th'], true)
            if ((start && !end) || (!start && end) || (start && end && start !== end)) {
              range.setCursor(false, true)
            }
          }
        }
        startTd = null
        me.removeListener('mouseover', mouseOverEvent)
      }
      me._selectionChange(250, evt)
    }

    function mouseOverEvent(type, evt) {
      if (isEditorDisabled()) {
        return
      }
      var me = this
      var tar = evt.target || evt.srcElement
      currentTd = domUtils.findParentByTagName(tar, 'td', true) || domUtils.findParentByTagName(tar, 'th', true)
      if (
        startTd &&
        currentTd &&
        ((startTd.tagName == 'TD' && currentTd.tagName == 'TD') ||
          (startTd.tagName == 'TH' && currentTd.tagName == 'TH')) &&
        domUtils.findParentByTagName(startTd, 'table') == domUtils.findParentByTagName(currentTd, 'table')
      ) {
        var ut = getUETable(currentTd)
        if (startTd != currentTd) {
          me.document.body.style.webkitUserSelect = 'none'
          me.selection.getNative()[browser.ie9below ? 'empty' : 'removeAllRanges']()
          var range = ut.getCellsRange(startTd, currentTd)
          ut.setSelected(range)
        } else {
          me.document.body.style.webkitUserSelect = ''
          ut.clearSelected()
        }
      }
      evt.preventDefault ? evt.preventDefault() : (evt.returnValue = false)
    }

    function setCellHeight(cell, height, backHeight) {
      var lineHight = parseInt(domUtils.getComputedStyle(cell, 'line-height'), 10)
      var tmpHeight = backHeight + height
      height = tmpHeight < lineHight ? lineHight : tmpHeight
      if (cell.style.height) {
        cell.style.height = ''
      }
      cell.rowSpan == 1 ? cell.setAttribute('height', height) : cell.removeAttribute && cell.removeAttribute('height')
    }

    function getWidth(cell) {
      if (!cell) {
        return 0
      }
      return parseInt(domUtils.getComputedStyle(cell, 'width'), 10)
    }

    function changeColWidth(cell, changeValue) {
      var ut = getUETable(cell)
      if (ut) {
        var table = ut.table
        var cells = getCellsByMoveBorder(cell, table)
        table.style.width = ''
        table.removeAttribute('width')
        changeValue = correctChangeValue(changeValue, cell, cells)
        if (cell.nextSibling) {
          var i = 0
          utils.each(cells, function(cellGroup) {
            cellGroup.left.width = +cellGroup.left.width + changeValue
            cellGroup.right && (cellGroup.right.width = +cellGroup.right.width - changeValue)
          })
        } else {
          utils.each(cells, function(cellGroup) {
            cellGroup.left.width -= -changeValue
          })
        }
      }
    }

    function isEditorDisabled() {
      return me.body.contentEditable === 'false'
    }

    function changeRowHeight(td, changeValue) {
      if (Math.abs(changeValue) < 10) {
        return
      }
      var ut = getUETable(td)
      if (ut) {
        var cells = ut.getSameEndPosCells(td, 'y')
        var backHeight = cells[0] ? cells[0].offsetHeight : 0
        for (var i = 0, cell; (cell = cells[i++]);) {
          setCellHeight(cell, changeValue, backHeight)
        }
      }
    }

    function getCellsByMoveBorder(cell, table, isContainMergeCell) {
      if (!table) {
        table = domUtils.findParentByTagName(cell, 'table')
      }
      if (!table) {
        return null
      }
      var index = domUtils.getNodeIndex(cell)
      var temp = cell
      var rows = table.rows
      var colIndex = 0
      while (temp) {
        if (temp.nodeType === 1) {
          colIndex += temp.colSpan || 1
        }
        temp = temp.previousSibling
      }
      temp = null
      var borderCells = []
      utils.each(rows, function(tabRow) {
        var cells = tabRow.cells
        var currIndex = 0
        utils.each(cells, function(tabCell) {
          currIndex += tabCell.colSpan || 1
          if (currIndex === colIndex) {
            borderCells.push({ left: tabCell, right: tabCell.nextSibling || null })
            return false
          } else {
            if (currIndex > colIndex) {
              if (isContainMergeCell) {
                borderCells.push({ left: tabCell })
              }
              return false
            }
          }
        })
      })
      return borderCells
    }

    function getMinWidthByTableCells(cells) {
      var minWidth = Number.MAX_VALUE
      for (var i = 0, curCell; (curCell = cells[i]); i++) {
        minWidth = Math.min(minWidth, curCell.width || getTableCellWidth(curCell))
      }
      return minWidth
    }

    function correctChangeValue(changeValue, relatedCell, cells) {
      changeValue -= getTabcellSpace()
      if (changeValue < 0) {
        return 0
      }
      changeValue -= getTableCellWidth(relatedCell)
      var direction = changeValue < 0 ? 'left' : 'right'
      changeValue = Math.abs(changeValue)
      utils.each(cells, function(cellGroup) {
        var curCell = cellGroup[direction]
        if (curCell) {
          changeValue = Math.min(changeValue, getTableCellWidth(curCell) - cellMinWidth)
        }
      })
      changeValue = changeValue < 0 ? 0 : changeValue
      return direction === 'left' ? -changeValue : changeValue
    }

    function getTableCellWidth(cell) {
      var width = 0
      var offset = 0
      var width = cell.offsetWidth - getTabcellSpace()
      if (!cell.nextSibling) {
        width -= getTableCellOffset(cell)
      }
      width = width < 0 ? 0 : width
      try {
        cell.width = width
      } catch (e) {
      }
      return width
    }

    function getTableCellOffset(cell) {
      tab = domUtils.findParentByTagName(cell, 'table', false)
      if (tab.offsetVal === undefined) {
        var prev = cell.previousSibling
        if (prev) {
          tab.offsetVal = cell.offsetWidth - prev.offsetWidth === UT.borderWidth ? UT.borderWidth : 0
        } else {
          tab.offsetVal = 0
        }
      }
      return tab.offsetVal
    }

    function getTabcellSpace() {
      if (UT.tabcellSpace === undefined) {
        var cell = null
        var tab = me.document.createElement('table')
        var tbody = me.document.createElement('tbody')
        var trow = me.document.createElement('tr')
        var tabcell = me.document.createElement('td')
        var mirror = null
        tabcell.style.cssText = 'border: 0;'
        tabcell.width = 1
        trow.appendChild(tabcell)
        trow.appendChild((mirror = tabcell.cloneNode(false)))
        tbody.appendChild(trow)
        tab.appendChild(tbody)
        tab.style.cssText = 'visibility: hidden;'
        me.body.appendChild(tab)
        UT.paddingSpace = tabcell.offsetWidth - 1
        var tmpTabWidth = tab.offsetWidth
        tabcell.style.cssText = ''
        mirror.style.cssText = ''
        UT.borderWidth = (tab.offsetWidth - tmpTabWidth) / 3
        UT.tabcellSpace = UT.paddingSpace + UT.borderWidth
        me.body.removeChild(tab)
      }
      getTabcellSpace = function() {
        return UT.tabcellSpace
      }
      return UT.tabcellSpace
    }

    function getDragLine(editor, doc) {
      if (mousedown) {
        return
      }
      dragLine = editor.document.createElement('div')
      domUtils.setAttributes(dragLine, {
        id: 'ue_tableDragLine',
        unselectable: 'on',
        contenteditable: false,
        onresizestart: 'return false',
        ondragstart: 'return false',
        onselectstart: 'return false',
        style:
          'background-color:blue;position:absolute;padding:0;margin:0;background-image:none;border:0px none;opacity:0;filter:alpha(opacity=0)'
      })
      editor.body.appendChild(dragLine)
    }

    function hideDragLine(editor) {
      if (mousedown) {
        return
      }
      var line
      while ((line = editor.document.getElementById('ue_tableDragLine'))) {
        domUtils.remove(line)
      }
    }

    function showDragLineAt(state, cell) {
      if (!cell) {
        return
      }
      var table = domUtils.findParentByTagName(cell, 'table')
      var caption = table.getElementsByTagName('caption')
      var width = table.offsetWidth
      var height = table.offsetHeight - (caption.length > 0 ? caption[0].offsetHeight : 0)
      var tablePos = domUtils.getXY(table)
      var cellPos = domUtils.getXY(cell)
      var css
      switch (state) {
        case 'h':
          css =
            'height:' +
            height +
            'px;top:' +
            (tablePos.y + (caption.length > 0 ? caption[0].offsetHeight : 0)) +
            'px;left:' +
            (cellPos.x + cell.offsetWidth)
          dragLine.style.cssText =
            css +
            'px;position: absolute;display:block;background-color:blue;width:1px;border:0; color:blue;opacity:.3;filter:alpha(opacity=30)'
          break
        case 'v':
          css = 'width:' + width + 'px;left:' + tablePos.x + 'px;top:' + (cellPos.y + cell.offsetHeight)
          dragLine.style.cssText =
            css +
            'px;overflow:hidden;position: absolute;display:block;background-color:blue;height:1px;border:0;color:blue;opacity:.2;filter:alpha(opacity=20)'
          break
        default:
      }
    }

    function switchBorderColor(editor, flag) {
      var tableArr = domUtils.getElementsByTagName(editor.body, 'table')
      var color
      for (var i = 0, node; (node = tableArr[i++]);) {
        var td = domUtils.getElementsByTagName(node, 'td')
        if (td[0]) {
          if (flag) {
            color = td[0].style.borderColor.replace(/\s/g, '')
            if (/(#ffffff)|(rgb\(255,255,255\))/gi.test(color)) {
              domUtils.addClass(node, 'noBorderTable')
            }
          } else {
            domUtils.removeClasses(node, 'noBorderTable')
          }
        }
      }
    }

    function getTableWidth(editor, needIEHack, defaultValue) {
      var body = editor.body
      return (
        body.offsetWidth -
        (needIEHack ? parseInt(domUtils.getComputedStyle(body, 'margin-left'), 10) * 2 : 0) -
        defaultValue.tableBorder * 2 -
        (editor.options.offsetWidth || 0)
      )
    }

    function getTargetTd(editor, evt) {
      var target = domUtils.findParentByTagName(evt.target || evt.srcElement, ['td', 'th'], true)
      var dir = null
      if (!target) {
        return null
      }
      dir = getRelation(target, mouseCoords(evt))
      if (!target) {
        return null
      }
      if (dir === 'h1' && target.previousSibling) {
        var position = domUtils.getXY(target)
        var cellWidth = target.offsetWidth
        if (Math.abs(position.x + cellWidth - evt.clientX) > cellWidth / 3) {
          target = target.previousSibling
        }
      } else {
        if (dir === 'v1' && target.parentNode.previousSibling) {
          var position = domUtils.getXY(target)
          var cellHeight = target.offsetHeight
          if (Math.abs(position.y + cellHeight - evt.clientY) > cellHeight / 3) {
            target = target.parentNode.previousSibling.firstChild
          }
        }
      }
      return target && !(editor.fireEvent('excludetable', target) === true) ? target : null
    }
  }
  UE.UETable.prototype.sortTable = function(sortByCellIndex, compareFn) {
    var table = this.table
    var rows = table.rows
    var trArray = []
    var flag = rows[0].cells[0].tagName === 'TH'
    var lastRowIndex = 0
    if (this.selectedTds.length) {
      var range = this.cellsRange
      var len = range.endRowIndex + 1
      for (var i = range.beginRowIndex; i < len; i++) {
        trArray[i] = rows[i]
      }
      trArray.splice(0, range.beginRowIndex)
      lastRowIndex = range.endRowIndex + 1 === this.rowsNum ? 0 : range.endRowIndex + 1
    } else {
      for (var i = 0, len = rows.length; i < len; i++) {
        trArray[i] = rows[i]
      }
    }
    var Fn = {
      reversecurrent: function(td1, td2) {
        return 1
      },
      orderbyasc: function(td1, td2) {
        var value1 = td1.innerText || td1.textContent
        var value2 = td2.innerText || td2.textContent
        return value1.localeCompare(value2)
      },
      reversebyasc: function(td1, td2) {
        var value1 = td1.innerHTML
        var value2 = td2.innerHTML
        return value2.localeCompare(value1)
      },
      orderbynum: function(td1, td2) {
        var value1 = td1[browser.ie ? 'innerText' : 'textContent'].match(/\d+/)
        var value2 = td2[browser.ie ? 'innerText' : 'textContent'].match(/\d+/)
        if (value1) {
          value1 = +value1[0]
        }
        if (value2) {
          value2 = +value2[0]
        }
        return (value1 || 0) - (value2 || 0)
      },
      reversebynum: function(td1, td2) {
        var value1 = td1[browser.ie ? 'innerText' : 'textContent'].match(/\d+/)
        var value2 = td2[browser.ie ? 'innerText' : 'textContent'].match(/\d+/)
        if (value1) {
          value1 = +value1[0]
        }
        if (value2) {
          value2 = +value2[0]
        }
        return (value2 || 0) - (value1 || 0)
      }
    }
    table.setAttribute('data-sort-type', compareFn && typeof compareFn === 'string' && Fn[compareFn] ? compareFn : '')
    flag && trArray.splice(0, 1)
    trArray = utils.sort(trArray, function(tr1, tr2) {
      var result
      if (compareFn && typeof compareFn === 'function') {
        result = compareFn.call(this, tr1.cells[sortByCellIndex], tr2.cells[sortByCellIndex])
      } else {
        if (compareFn && typeof compareFn === 'number') {
          result = 1
        } else {
          if (compareFn && typeof compareFn === 'string' && Fn[compareFn]) {
            result = Fn[compareFn].call(this, tr1.cells[sortByCellIndex], tr2.cells[sortByCellIndex])
          } else {
            result = Fn['orderbyasc'].call(this, tr1.cells[sortByCellIndex], tr2.cells[sortByCellIndex])
          }
        }
      }
      return result
    })
    var fragment = table.ownerDocument.createDocumentFragment()
    for (var j = 0, len = trArray.length; j < len; j++) {
      fragment.appendChild(trArray[j])
    }
    var tbody = table.getElementsByTagName('tbody')[0]
    if (!lastRowIndex) {
      tbody.appendChild(fragment)
    } else {
      tbody.insertBefore(fragment, rows[lastRowIndex - range.endRowIndex + range.beginRowIndex - 1])
    }
  }
  UE.plugins['tablesort'] = function() {
    var me = this
    var UT = UE.UETable
    var getUETable = function(tdOrTable) {
      return UT.getUETable(tdOrTable)
    }
    var getTableItemsByRange = function(editor) {
      return UT.getTableItemsByRange(editor)
    }
    me.ready(function() {
      utils.cssRule(
        'tablesort',
        'table.sortEnabled tr.firstRow th,table.sortEnabled tr.firstRow td{padding-right:20px;background-repeat: no-repeat;background-position: center right;' +
        '   background-image:url(' +
        me.options.themePath +
        me.options.theme +
        '/images/sortable.png);}',
        me.document
      )
      me.addListener('afterexeccommand', function(type, cmd) {
        if (cmd == 'mergeright' || cmd == 'mergedown' || cmd == 'mergecells') {
          this.execCommand('disablesort')
        }
      })
    })
    UE.commands['sorttable'] = {
      queryCommandState: function() {
        var me = this
        var tableItems = getTableItemsByRange(me)
        if (!tableItems.cell) {
          return -1
        }
        var table = tableItems.table
        var cells = table.getElementsByTagName('td')
        for (var i = 0, cell; (cell = cells[i++]);) {
          if (cell.rowSpan != 1 || cell.colSpan != 1) {
            return -1
          }
        }
        return 0
      },
      execCommand: function(cmd, fn) {
        var me = this
        var range = me.selection.getRange()
        var bk = range.createBookmark(true)
        var tableItems = getTableItemsByRange(me)
        var cell = tableItems.cell
        var ut = getUETable(tableItems.table)
        var cellInfo = ut.getCellInfo(cell)
        ut.sortTable(cellInfo.cellIndex, fn)
        range.moveToBookmark(bk)
        try {
          range.select()
        } catch (e) {
        }
      }
    }
    UE.commands['enablesort'] = UE.commands['disablesort'] = {
      queryCommandState: function(cmd) {
        var table = getTableItemsByRange(this).table
        if (table && cmd == 'enablesort') {
          var cells = domUtils.getElementsByTagName(table, 'th td')
          for (var i = 0; i < cells.length; i++) {
            if (cells[i].getAttribute('colspan') > 1 || cells[i].getAttribute('rowspan') > 1) {
              return -1
            }
          }
        }
        return !table ? -1 : (cmd == 'enablesort') ^ (table.getAttribute('data-sort') != 'sortEnabled') ? -1 : 0
      },
      execCommand: function(cmd) {
        var table = getTableItemsByRange(this).table
        table.setAttribute('data-sort', cmd == 'enablesort' ? 'sortEnabled' : 'sortDisabled')
        cmd == 'enablesort' ? domUtils.addClass(table, 'sortEnabled') : domUtils.removeClasses(table, 'sortEnabled')
      }
    }
  }
  UE.plugins['contextmenu'] = function() {
    var me = this
    me.setOpt('enableContextMenu', true)
    if (me.getOpt('enableContextMenu') === false) {
      return
    }
    var lang = me.getLang('contextMenu')
    var menu
    var items = me.options.contextMenu || [
      { label: lang['selectall'], cmdName: 'selectall' },
      {
        label: lang.cleardoc,
        cmdName: 'cleardoc',
        exec: function() {
          if (confirm(lang.confirmclear)) {
            this.execCommand('cleardoc')
          }
        }
      },
      '-',
      { label: lang.unlink, cmdName: 'unlink' },
      '-',
      {
        group: lang.paragraph,
        icon: 'justifyjustify',
        subMenu: [
          { label: lang.justifyleft, cmdName: 'justify', value: 'left' },
          { label: lang.justifyright, cmdName: 'justify', value: 'right' },
          { label: lang.justifycenter, cmdName: 'justify', value: 'center' },
          { label: lang.justifyjustify, cmdName: 'justify', value: 'justify' }
        ]
      },
      '-',
      {
        group: lang.table,
        icon: 'table',
        subMenu: [
          { label: lang.inserttable, cmdName: 'inserttable' },
          { label: lang.deletetable, cmdName: 'deletetable' },
          '-',
          { label: lang.deleterow, cmdName: 'deleterow' },
          { label: lang.deletecol, cmdName: 'deletecol' },
          { label: lang.insertcol, cmdName: 'insertcol' },
          { label: lang.insertcolnext, cmdName: 'insertcolnext' },
          { label: lang.insertrow, cmdName: 'insertrow' },
          { label: lang.insertrownext, cmdName: 'insertrownext' },
          '-',
          { label: lang.insertcaption, cmdName: 'insertcaption' },
          { label: lang.deletecaption, cmdName: 'deletecaption' },
          { label: lang.inserttitle, cmdName: 'inserttitle' },
          { label: lang.deletetitle, cmdName: 'deletetitle' },
          { label: lang.inserttitlecol, cmdName: 'inserttitlecol' },
          { label: lang.deletetitlecol, cmdName: 'deletetitlecol' },
          '-',
          { label: lang.mergecells, cmdName: 'mergecells' },
          { label: lang.mergeright, cmdName: 'mergeright' },
          { label: lang.mergedown, cmdName: 'mergedown' },
          '-',
          { label: lang.splittorows, cmdName: 'splittorows' },
          { label: lang.splittocols, cmdName: 'splittocols' },
          { label: lang.splittocells, cmdName: 'splittocells' },
          '-',
          { label: lang.averageDiseRow, cmdName: 'averagedistributerow' },
          { label: lang.averageDisCol, cmdName: 'averagedistributecol' },
          '-',
          {
            label: lang.edittd,
            cmdName: 'edittd',
            exec: function() {
              if (UE.ui['edittd']) {
                new UE.ui['edittd'](this)
              }
              this.getDialog('edittd').open()
            }
          },
          {
            label: lang.edittable,
            cmdName: 'edittable',
            exec: function() {
              if (UE.ui['edittable']) {
                new UE.ui['edittable'](this)
              }
              this.getDialog('edittable').open()
            }
          },
          { label: lang.setbordervisible, cmdName: 'setbordervisible' }
        ]
      },
      {
        group: lang.tablesort,
        icon: 'tablesort',
        subMenu: [
          { label: lang.enablesort, cmdName: 'enablesort' },
          { label: lang.disablesort, cmdName: 'disablesort' },
          '-',
          { label: lang.reversecurrent, cmdName: 'sorttable', value: 'reversecurrent' },
          { label: lang.orderbyasc, cmdName: 'sorttable', value: 'orderbyasc' },
          { label: lang.reversebyasc, cmdName: 'sorttable', value: 'reversebyasc' },
          { label: lang.orderbynum, cmdName: 'sorttable', value: 'orderbynum' },
          { label: lang.reversebynum, cmdName: 'sorttable', value: 'reversebynum' }
        ]
      },
      {
        group: lang.borderbk,
        icon: 'borderBack',
        subMenu: [
          {
            label: lang.setcolor,
            cmdName: 'interlacetable',
            exec: function() {
              this.execCommand('interlacetable')
            }
          },
          {
            label: lang.unsetcolor,
            cmdName: 'uninterlacetable',
            exec: function() {
              this.execCommand('uninterlacetable')
            }
          },
          {
            label: lang.setbackground,
            cmdName: 'settablebackground',
            exec: function() {
              this.execCommand('settablebackground', { repeat: true, colorList: ['#bbb', '#ccc'] })
            }
          },
          {
            label: lang.unsetbackground,
            cmdName: 'cleartablebackground',
            exec: function() {
              this.execCommand('cleartablebackground')
            }
          },
          {
            label: lang.redandblue,
            cmdName: 'settablebackground',
            exec: function() {
              this.execCommand('settablebackground', { repeat: true, colorList: ['red', 'blue'] })
            }
          },
          {
            label: lang.threecolorgradient,
            cmdName: 'settablebackground',
            exec: function() {
              this.execCommand('settablebackground', { repeat: true, colorList: ['#aaa', '#bbb', '#ccc'] })
            }
          }
        ]
      },
      {
        group: lang.aligntd,
        icon: 'aligntd',
        subMenu: [
          { cmdName: 'cellalignment', value: { align: 'left', vAlign: 'top' } },
          { cmdName: 'cellalignment', value: { align: 'center', vAlign: 'top' } },
          { cmdName: 'cellalignment', value: { align: 'right', vAlign: 'top' } },
          { cmdName: 'cellalignment', value: { align: 'left', vAlign: 'middle' } },
          { cmdName: 'cellalignment', value: { align: 'center', vAlign: 'middle' } },
          { cmdName: 'cellalignment', value: { align: 'right', vAlign: 'middle' } },
          { cmdName: 'cellalignment', value: { align: 'left', vAlign: 'bottom' } },
          { cmdName: 'cellalignment', value: { align: 'center', vAlign: 'bottom' } },
          { cmdName: 'cellalignment', value: { align: 'right', vAlign: 'bottom' } }
        ]
      },
      {
        group: lang.aligntable,
        icon: 'aligntable',
        subMenu: [
          { cmdName: 'tablealignment', className: 'left', label: lang.tableleft, value: 'left' },
          { cmdName: 'tablealignment', className: 'center', label: lang.tablecenter, value: 'center' },
          { cmdName: 'tablealignment', className: 'right', label: lang.tableright, value: 'right' }
        ]
      },
      '-',
      { label: lang.insertparagraphbefore, cmdName: 'insertparagraph', value: true },
      { label: lang.insertparagraphafter, cmdName: 'insertparagraph' },
      { label: lang.copy, cmdName: 'copy' },
      { label: lang.cut_text, cmdName: 'cut_text', icon: 'paste' },
      { label: lang.paste, cmdName: 'paste' }
    ]
    if (!items.length) {
      return
    }
    var uiUtils = UE.ui.uiUtils
    me.addListener('contextmenu', function(type, evt) {
      var offset = uiUtils.getViewportOffsetByEvent(evt)
      me.fireEvent('beforeselectionchange')
      if (menu) {
        menu.destroy()
      }
      for (var i = 0, ti, contextItems = []; (ti = items[i]); i++) {
        var last
        ;(function(item) {
          if (item == '-') {
            if ((last = contextItems[contextItems.length - 1]) && last !== '-') {
              contextItems.push('-')
            }
          } else {
            if (item.hasOwnProperty('group')) {
              for (var j = 0, cj, subMenu = []; (cj = item.subMenu[j]); j++) {
                ;(function(subItem) {
                  if (subItem == '-') {
                    if ((last = subMenu[subMenu.length - 1]) && last !== '-') {
                      subMenu.push('-')
                    } else {
                      subMenu.splice(subMenu.length - 1)
                    }
                  } else {
                    if (
                      (me.commands[subItem.cmdName] || UE.commands[subItem.cmdName] || subItem.query) &&
                      (subItem.query ? subItem.query() : me.queryCommandState(subItem.cmdName)) > -1
                    ) {
                      subMenu.push({
                        label:
                          subItem.label || me.getLang('contextMenu.' + subItem.cmdName + (subItem.value || '')) || '',
                        className:
                          'edui-for-' +
                          subItem.cmdName +
                          (subItem.className ? ' edui-for-' + subItem.cmdName + '-' + subItem.className : ''),
                        onclick: subItem.exec
                          ? function() {
                            subItem.exec.call(me)
                          }
                          : function() {
                            me.execCommand(subItem.cmdName, subItem.value)
                          }
                      })
                    }
                  }
                })(cj)
              }
              if (subMenu.length) {
                function getLabel() {
                  switch (item.icon) {
                    case 'table':
                      return me.getLang('contextMenu.table')
                    case 'justifyjustify':
                      return me.getLang('contextMenu.paragraph')
                    case 'aligntd':
                      return me.getLang('contextMenu.aligntd')
                    case 'aligntable':
                      return me.getLang('contextMenu.aligntable')
                    case 'tablesort':
                      return lang.tablesort
                    case 'borderBack':
                      return lang.borderbk
                    default:
                      return ''
                  }
                }

                contextItems.push({
                  label: getLabel(),
                  className: 'edui-for-' + item.icon,
                  subMenu: { items: subMenu, editor: me }
                })
              }
            } else {
              if (
                (me.commands[item.cmdName] || UE.commands[item.cmdName] || item.query) &&
                (item.query ? item.query.call(me) : me.queryCommandState(item.cmdName)) > -1
              ) {
                contextItems.push({
                  label: item.label || me.getLang('contextMenu.' + item.cmdName),
                  className: 'edui-for-' + (item.icon ? item.icon : item.cmdName + (item.value || '')),
                  onclick: item.exec
                    ? function() {
                      item.exec.call(me)
                    }
                    : function() {
                      me.execCommand(item.cmdName, item.value)
                    }
                })
              }
            }
          }
        })(ti)
      }
      if (contextItems[contextItems.length - 1] == '-') {
        contextItems.pop()
      }
      menu = new UE.ui.Menu({ items: contextItems, className: 'edui-contextmenu', editor: me })
      menu.render()
      menu.showAt(offset)
      me.fireEvent('aftershowcontextmenu', menu)
      domUtils.preventDefault(evt)
      if (browser.ie) {
        var ieRange
        try {
          ieRange = me.selection.getNative().createRange()
        } catch (e) {
          return
        }
        if (ieRange.item) {
          var range = new dom.Range(me.document)
          range.selectNode(ieRange.item(0)).select(true, true)
        }
      }
    })
    me.addListener('aftershowcontextmenu', function(type, menu) {
      if (me.zeroclipboard) {
        var items = menu.items
        for (var key in items) {
          if (items[key].className == 'edui-for-copy') {
            me.zeroclipboard.clip(items[key].getDom())
          }
        }
      }
    })
  }
  UE.plugins['shortcutmenu'] = function() {
    var me = this
    var menu
    var items = me.options.shortcutMenu || []
    if (!items.length) {
      return
    }
    me.addListener('contextmenu mouseup', function(type, e) {
      var me = this
      var customEvt = {
        type: type,
        target: e.target || e.srcElement,
        screenX: e.screenX,
        screenY: e.screenY,
        clientX: e.clientX,
        clientY: e.clientY
      }
      setTimeout(function() {
        var rng = me.selection.getRange()
        if (rng.collapsed === false || type == 'contextmenu') {
          if (!menu) {
            menu = new baidu.editor.ui.ShortCutMenu({
              editor: me,
              items: items,
              theme: me.options.theme,
              className: 'edui-shortcutmenu'
            })
            menu.render()
            me.fireEvent('afterrendershortcutmenu', menu)
          }
          menu.show(customEvt, !!UE.plugins['contextmenu'])
        }
      })
      if (type == 'contextmenu') {
        domUtils.preventDefault(e)
        if (browser.ie9below) {
          var ieRange
          try {
            ieRange = me.selection.getNative().createRange()
          } catch (e) {
            return
          }
          if (ieRange.item) {
            var range = new dom.Range(me.document)
            range.selectNode(ieRange.item(0)).select(true, true)
          }
        }
      }
    })
    me.addListener('keydown', function(type) {
      if (type == 'keydown') {
        menu && !menu.isHidden && menu.hide()
      }
    })
  }
  UE.plugins['basestyle'] = function() {
    var basestyles = { bold: ['strong', 'b'], italic: ['em', 'i'], subscript: ['sub'], superscript: ['sup'] }
    var getObj = function(editor, tagNames) {
      return domUtils.filterNodeList(editor.selection.getStartElementPath(), tagNames)
    }
    var me = this
    me.addshortcutkey({ Bold: 'ctrl+66', Italic: 'ctrl+73', Underline: 'ctrl+85' })
    me.addInputRule(function(root) {
      utils.each(root.getNodesByTagName('b i'), function(node) {
        switch (node.tagName) {
          case 'b':
            node.tagName = 'strong'
            break
          case 'i':
            node.tagName = 'em'
        }
      })
    })
    for (var style in basestyles) {
      ;(function(cmd, tagNames) {
        me.commands[cmd] = {
          execCommand: function(cmdName) {
            var range = me.selection.getRange()
            var obj = getObj(this, tagNames)
            if (range.collapsed) {
              if (obj) {
                var tmpText = me.document.createTextNode('')
                range.insertNode(tmpText).removeInlineStyle(tagNames)
                range.setStartBefore(tmpText)
                domUtils.remove(tmpText)
              } else {
                var tmpNode = range.document.createElement(tagNames[0])
                if (cmdName == 'superscript' || cmdName == 'subscript') {
                  tmpText = me.document.createTextNode('')
                  range
                    .insertNode(tmpText)
                    .removeInlineStyle(['sub', 'sup'])
                    .setStartBefore(tmpText)
                    .collapse(true)
                }
                range.insertNode(tmpNode).setStart(tmpNode, 0)
              }
              range.collapse(true)
            } else {
              if (cmdName == 'superscript' || cmdName == 'subscript') {
                if (!obj || obj.tagName.toLowerCase() != cmdName) {
                  range.removeInlineStyle(['sub', 'sup'])
                }
              }
              obj ? range.removeInlineStyle(tagNames) : range.applyInlineStyle(tagNames[0])
            }
            range.select()
          },
          queryCommandState: function() {
            return getObj(this, tagNames) ? 1 : 0
          }
        }
      })(style, basestyles[style])
    }
  }
  UE.plugins['elementpath'] = function() {
    var currentLevel
    var tagNames
    var me = this
    me.setOpt('elementPathEnabled', true)
    if (!me.options.elementPathEnabled) {
      return
    }
    me.commands['elementpath'] = {
      execCommand: function(cmdName, level) {
        var start = tagNames[level]
        var range = me.selection.getRange()
        currentLevel = level * 1
        range.selectNode(start).select()
      },
      queryCommandValue: function() {
        var parents = [].concat(this.selection.getStartElementPath()).reverse()
        var names = []
        tagNames = parents
        for (var i = 0, ci; (ci = parents[i]); i++) {
          if (ci.nodeType == 3) {
            continue
          }
          var name = ci.tagName.toLowerCase()
          if (name == 'img' && ci.getAttribute('anchorname')) {
            name = 'anchor'
          }
          names[i] = name
          if (currentLevel == i) {
            currentLevel = -1
            break
          }
        }
        return names
      }
    }
  }
  UE.plugins['formatmatch'] = function() {
    var me = this
    var list = []
    var img
    var flag = 0
    me.addListener('reset', function() {
      list = []
      flag = 0
    })

    function addList(type, evt) {
      if (browser.webkit) {
        var target = evt.target.tagName == 'IMG' ? evt.target : null
      }

      function addFormat(range) {
        if (text) {
          range.selectNode(text)
        }
        return range.applyInlineStyle(list[list.length - 1].tagName, null, list)
      }

      me.undoManger && me.undoManger.save()
      var range = me.selection.getRange()
      var imgT = target || range.getClosedNode()
      if (img && imgT && imgT.tagName == 'IMG') {
        imgT.style.cssText +=
          ';float:' +
          (img.style.cssFloat || img.style.styleFloat || 'none') +
          ';display:' +
          (img.style.display || 'inline')
        img = null
      } else {
        if (!img) {
          var collapsed = range.collapsed
          if (collapsed) {
            var text = me.document.createTextNode('match')
            range.insertNode(text).select()
          }
          me.__hasEnterExecCommand = true
          var removeFormatAttributes = me.options.removeFormatAttributes
          me.options.removeFormatAttributes = ''
          me.execCommand('removeformat')
          me.options.removeFormatAttributes = removeFormatAttributes
          me.__hasEnterExecCommand = false
          range = me.selection.getRange()
          if (list.length) {
            addFormat(range)
          }
          if (text) {
            range.setStartBefore(text).collapse(true)
          }
          range.select()
          text && domUtils.remove(text)
        }
      }
      me.undoManger && me.undoManger.save()
      me.removeListener('mouseup', addList)
      flag = 0
    }

    me.commands['formatmatch'] = {
      execCommand: function(cmdName) {
        if (flag) {
          flag = 0
          list = []
          me.removeListener('mouseup', addList)
          return
        }
        var range = me.selection.getRange()
        img = range.getClosedNode()
        if (!img || img.tagName != 'IMG') {
          range.collapse(true).shrinkBoundary()
          var start = range.startContainer
          list = domUtils.findParents(start, true, function(node) {
            return !domUtils.isBlockElm(node) && node.nodeType == 1
          })
          for (var i = 0, ci; (ci = list[i]); i++) {
            if (ci.tagName == 'A') {
              list.splice(i, 1)
              break
            }
          }
        }
        me.addListener('mouseup', addList)
        flag = 1
      },
      queryCommandState: function() {
        return flag
      },
      notNeedUndo: 1
    }
  }
  UE.plugin.register('searchreplace', function() {
    var me = this
    var _blockElm = { table: 1, tbody: 1, tr: 1, ol: 1, ul: 1 }

    function findTextInString(textContent, opt, currentIndex) {
      var str = opt.searchStr
      if (opt.dir == -1) {
        textContent = textContent
          .split('')
          .reverse()
          .join('')
        str = str
          .split('')
          .reverse()
          .join('')
        currentIndex = textContent.length - currentIndex
      }
      var reg = new RegExp(str, 'g' + (opt.casesensitive ? '' : 'i'))
      var match
      while ((match = reg.exec(textContent))) {
        if (match.index >= currentIndex) {
          return opt.dir == -1 ? textContent.length - match.index - opt.searchStr.length : match.index
        }
      }
      return -1
    }

    function findTextBlockElm(node, currentIndex, opt) {
      var textContent
      var index
      var methodName = opt.all || opt.dir == 1 ? 'getNextDomNode' : 'getPreDomNode'
      if (domUtils.isBody(node)) {
        node = node.firstChild
      }
      var first = 1
      while (node) {
        textContent = node.nodeType == 3 ? node.nodeValue : node[browser.ie ? 'innerText' : 'textContent']
        index = findTextInString(textContent, opt, currentIndex)
        first = 0
        if (index != -1) {
          return { node: node, index: index }
        }
        node = domUtils[methodName](node)
        while (node && _blockElm[node.nodeName.toLowerCase()]) {
          node = domUtils[methodName](node, true)
        }
        if (node) {
          currentIndex =
            opt.dir == -1
              ? (node.nodeType == 3 ? node.nodeValue : node[browser.ie ? 'innerText' : 'textContent']).length
              : 0
        }
      }
    }

    function findNTextInBlockElm(node, index, str) {
      var currentIndex = 0
      var currentNode = node.firstChild
      var currentNodeLength = 0
      var result
      while (currentNode) {
        if (currentNode.nodeType == 3) {
          currentNodeLength = currentNode.nodeValue.replace(/(^[\t\r\n]+)|([\t\r\n]+$)/, '').length
          currentIndex += currentNodeLength
          if (currentIndex >= index) {
            return { node: currentNode, index: currentNodeLength - (currentIndex - index) }
          }
        } else {
          if (!dtd.$empty[currentNode.tagName]) {
            currentNodeLength = currentNode[browser.ie ? 'innerText' : 'textContent'].replace(
              /(^[\t\r\n]+)|([\t\r\n]+$)/,
              ''
            ).length
            currentIndex += currentNodeLength
            if (currentIndex >= index) {
              result = findNTextInBlockElm(currentNode, currentNodeLength - (currentIndex - index), str)
              if (result) {
                return result
              }
            }
          }
        }
        currentNode = domUtils.getNextDomNode(currentNode)
      }
    }

    function searchReplace(me, opt) {
      var rng = me.selection.getRange()
      var startBlockNode
      var searchStr = opt.searchStr
      var span = me.document.createElement('span')
      span.innerHTML = '$$ueditor_searchreplace_key$$'
      rng.shrinkBoundary(true)
      if (!rng.collapsed) {
        rng.select()
        var rngText = me.selection.getText()
        if (new RegExp('^' + opt.searchStr + '$', opt.casesensitive ? '' : 'i').test(rngText)) {
          if (opt.replaceStr != undefined) {
            replaceText(rng, opt.replaceStr)
            rng.select()
            return true
          } else {
            rng.collapse(opt.dir == -1)
          }
        }
      }
      rng.insertNode(span)
      rng.enlargeToBlockElm(true)
      startBlockNode = rng.startContainer
      var currentIndex = startBlockNode[browser.ie ? 'innerText' : 'textContent'].indexOf(
        '$$ueditor_searchreplace_key$$'
      )
      rng.setStartBefore(span)
      domUtils.remove(span)
      var result = findTextBlockElm(startBlockNode, currentIndex, opt)
      if (result) {
        var rngStart = findNTextInBlockElm(result.node, result.index, searchStr)
        var rngEnd = findNTextInBlockElm(result.node, result.index + searchStr.length, searchStr)
        rng.setStart(rngStart.node, rngStart.index).setEnd(rngEnd.node, rngEnd.index)
        if (opt.replaceStr !== undefined) {
          replaceText(rng, opt.replaceStr)
        }
        rng.select()
        return true
      } else {
        rng.setCursor()
      }
    }

    function replaceText(rng, str) {
      str = me.document.createTextNode(str)
      rng.deleteContents().insertNode(str)
    }

    return {
      commands: {
        searchreplace: {
          execCommand: function(cmdName, opt) {
            utils.extend(opt, { all: false, casesensitive: false, dir: 1 }, true)
            var num = 0
            if (opt.all) {
              var rng = me.selection.getRange()
              var first = me.body.firstChild
              if (first && first.nodeType == 1) {
                rng.setStart(first, 0)
                rng.shrinkBoundary(true)
              } else {
                if (first.nodeType == 3) {
                  rng.setStartBefore(first)
                }
              }
              rng.collapse(true).select(true)
              if (opt.replaceStr !== undefined) {
                me.fireEvent('saveScene')
              }
              while (searchReplace(this, opt)) {
                num++
              }
              if (num) {
                me.fireEvent('saveScene')
              }
            } else {
              if (opt.replaceStr !== undefined) {
                me.fireEvent('saveScene')
              }
              if (searchReplace(this, opt)) {
                num++
              }
              if (num) {
                me.fireEvent('saveScene')
              }
            }
            return num
          },
          notNeedUndo: 1
        }
      }
    }
  })
  UE.plugins['customstyle'] = function() {
    var me = this
    me.setOpt({
      customstyle: [
        {
          tag: 'h1',
          name: 'tc',
          style:
            'font-size:32px;font-weight:bold;border-bottom:#ccc 2px solid;padding:0 4px 0 0;text-align:center;margin:0 0 20px 0;'
        },
        {
          tag: 'h1',
          name: 'tl',
          style:
            'font-size:32px;font-weight:bold;border-bottom:#ccc 2px solid;padding:0 4px 0 0;text-align:left;margin:0 0 10px 0;'
        },
        { tag: 'span', name: 'im', style: 'font-size:18px;font-style:italic;font-weight:bold;line-height:18px;' },
        {
          tag: 'span',
          name: 'hi',
          style: 'font-size:18px;font-style:italic;font-weight:bold;color:rgb(51, 153, 204);line-height:18px;'
        }
      ]
    })
    me.commands['customstyle'] = {
      execCommand: function(cmdName, obj) {
        var me = this
        var tagName = obj.tag
        var node = domUtils.findParent(
          me.selection.getStart(),
          function(node) {
            return node.getAttribute('label')
          },
          true
        )
        var range
        var bk
        var tmpObj = {}
        for (var p in obj) {
          if (obj[p] !== undefined) {
            tmpObj[p] = obj[p]
          }
        }
        delete tmpObj.tag
        if (node && node.getAttribute('label') == obj.label) {
          range = this.selection.getRange()
          bk = range.createBookmark()
          if (range.collapsed) {
            if (dtd.$block[node.tagName]) {
              var fillNode = me.document.createElement('p')
              domUtils.moveChild(node, fillNode)
              node.parentNode.insertBefore(fillNode, node)
              domUtils.remove(node)
            } else {
              domUtils.remove(node, true)
            }
          } else {
            var common = domUtils.getCommonAncestor(bk.start, bk.end)
            var nodes = domUtils.getElementsByTagName(common, tagName)
            if (new RegExp(tagName, 'i').test(common.tagName)) {
              nodes.push(common)
            }
            for (var i = 0, ni; (ni = nodes[i++]);) {
              if (ni.getAttribute('label') == obj.label) {
                var ps = domUtils.getPosition(ni, bk.start)
                var pe = domUtils.getPosition(ni, bk.end)
                if (
                  (ps & domUtils.POSITION_FOLLOWING || ps & domUtils.POSITION_CONTAINS) &&
                  (pe & domUtils.POSITION_PRECEDING || pe & domUtils.POSITION_CONTAINS)
                ) {
                  if (dtd.$block[tagName]) {
                    var fillNode = me.document.createElement('p')
                    domUtils.moveChild(ni, fillNode)
                    ni.parentNode.insertBefore(fillNode, ni)
                  }
                }
                domUtils.remove(ni, true)
              }
            }
            node = domUtils.findParent(
              common,
              function(node) {
                return node.getAttribute('label') == obj.label
              },
              true
            )
            if (node) {
              domUtils.remove(node, true)
            }
          }
          range.moveToBookmark(bk).select()
        } else {
          if (dtd.$block[tagName]) {
            this.execCommand('paragraph', tagName, tmpObj, 'customstyle')
            range = me.selection.getRange()
            if (!range.collapsed) {
              range.collapse()
              node = domUtils.findParent(
                me.selection.getStart(),
                function(node) {
                  return node.getAttribute('label') == obj.label
                },
                true
              )
              var pNode = me.document.createElement('p')
              domUtils.insertAfter(node, pNode)
              domUtils.fillNode(me.document, pNode)
              range.setStart(pNode, 0).setCursor()
            }
          } else {
            range = me.selection.getRange()
            if (range.collapsed) {
              node = me.document.createElement(tagName)
              domUtils.setAttributes(node, tmpObj)
              range
                .insertNode(node)
                .setStart(node, 0)
                .setCursor()
              return
            }
            bk = range.createBookmark()
            range
              .applyInlineStyle(tagName, tmpObj)
              .moveToBookmark(bk)
              .select()
          }
        }
      },
      queryCommandValue: function() {
        var parent = domUtils.filterNodeList(this.selection.getStartElementPath(), function(node) {
          return node.getAttribute('label')
        })
        return parent ? parent.getAttribute('label') : ''
      }
    }
    me.addListener('keyup', function(type, evt) {
      var keyCode = evt.keyCode || evt.which
      if (keyCode == 32 || keyCode == 13) {
        var range = me.selection.getRange()
        if (range.collapsed) {
          var node = domUtils.findParent(
            me.selection.getStart(),
            function(node) {
              return node.getAttribute('label')
            },
            true
          )
          if (node && dtd.$block[node.tagName] && domUtils.isEmptyNode(node)) {
            var p = me.document.createElement('p')
            domUtils.insertAfter(node, p)
            domUtils.fillNode(me.document, p)
            domUtils.remove(node)
            range.setStart(p, 0).setCursor()
          }
        }
      }
    })
  }
  UE.plugins['catchremoteimage'] = function() {
    var me = this
    var ajax = UE.ajax
    if (me.options.catchRemoteImageEnable === false) {
      return
    }
    me.setOpt({ catchRemoteImageEnable: false })
    me.addListener('afterpaste', function() {
      me.fireEvent('catchRemoteImage')
    })
    me.addListener('catchRemoteImage', function() {
      var catcherLocalDomain = me.getOpt('catcherLocalDomain')
      var catcherActionUrl = me.getActionUrl(me.getOpt('catcherActionName'))
      var catcherUrlPrefix = me.getOpt('catcherUrlPrefix')
      var catcherFieldName = me.getOpt('catcherFieldName')
      var remoteImages = []
      var imgs = domUtils.getElementsByTagName(me.document, 'img')
      var test = function(src, urls) {
        if (src.indexOf(location.host) != -1 || /(^\.)|(^\/)/.test(src)) {
          return true
        }
        if (urls) {
          for (var j = 0, url; (url = urls[j++]);) {
            if (src.indexOf(url) !== -1) {
              return true
            }
          }
        }
        return false
      }
      for (var i = 0, ci; (ci = imgs[i++]);) {
        if (ci.getAttribute('word_img')) {
          continue
        }
        var src = ci.getAttribute('_src') || ci.src || ''
        if (/^(https?|ftp):/i.test(src) && !test(src, catcherLocalDomain)) {
          remoteImages.push(src)
        }
      }
      if (remoteImages.length) {
        catchremoteimage(remoteImages, {
          success: function(r) {
            try {
              var info = r.state !== undefined ? r : eval('(' + r.responseText + ')')
            } catch (e) {
              return
            }
            var i
            var j
            var ci
            var cj
            var oldSrc
            var newSrc
            var list = info.list
            for (i = 0; (ci = imgs[i++]);) {
              oldSrc = ci.getAttribute('_src') || ci.src || ''
              for (j = 0; (cj = list[j++]);) {
                if (oldSrc == cj.source && cj.state == 'SUCCESS') {
                  newSrc = catcherUrlPrefix + cj.url
                  domUtils.setAttributes(ci, { src: newSrc, _src: newSrc })
                  break
                }
              }
            }
            me.fireEvent('catchremotesuccess')
          },
          error: function() {
            me.fireEvent('catchremoteerror')
          }
        })
      }

      function catchremoteimage(imgs, callbacks) {
        var params = utils.serializeParam(me.queryCommandValue('serverparam')) || ''
        var url = utils.formatUrl(catcherActionUrl + (catcherActionUrl.indexOf('?') == -1 ? '?' : '&') + params)
        var isJsonp = utils.isCrossDomainUrl(url)
        var opt = {
          method: 'POST',
          dataType: isJsonp ? 'jsonp' : '',
          timeout: 60000,
          onsuccess: callbacks['success'],
          onerror: callbacks['error']
        }
        opt[catcherFieldName] = imgs
        ajax.request(url, opt)
      }
    })
  }
  UE.plugin.register('snapscreen', function() {
    var me = this
    var snapplugin

    function getLocation(url) {
      var search
      var a = document.createElement('a')
      var params = utils.serializeParam(me.queryCommandValue('serverparam')) || ''
      a.href = url
      if (browser.ie) {
        a.href = a.href
      }
      search = a.search
      if (params) {
        search = search + (search.indexOf('?') == -1 ? '?' : '&') + params
        search = search.replace(/[&]+/gi, '&')
      }
      return { port: a.port, hostname: a.hostname, path: a.pathname + search || +a.hash }
    }

    return {
      commands: {
        snapscreen: {
          execCommand: function(cmd) {
            var url, local, res
            var lang = me.getLang('snapScreen_plugin')
            if (!snapplugin) {
              var container = me.container
              var doc = me.container.ownerDocument || me.container.document
              snapplugin = doc.createElement('object')
              try {
                snapplugin.type = 'application/x-pluginbaidusnap'
              } catch (e) {
                return
              }
              snapplugin.style.cssText = 'position:absolute;left:-9999px;width:0;height:0;'
              snapplugin.setAttribute('width', '0')
              snapplugin.setAttribute('height', '0')
              container.appendChild(snapplugin)
            }

            function onSuccess(rs) {
              try {
                rs = eval('(' + rs + ')')
                if (rs.state == 'SUCCESS') {
                  var opt = me.options
                  me.execCommand('insertimage', {
                    src: opt.snapscreenUrlPrefix + rs.url,
                    _src: opt.snapscreenUrlPrefix + rs.url,
                    alt: rs.title || '',
                    floatStyle: opt.snapscreenImgAlign
                  })
                } else {
                  alert(rs.state)
                }
              } catch (e) {
                alert(lang.callBackErrorMsg)
              }
            }

            url = me.getActionUrl(me.getOpt('snapscreenActionName'))
            local = getLocation(url)
            setTimeout(function() {
              try {
                res = snapplugin.saveSnapshot(local.hostname, local.path, local.port)
              } catch (e) {
                me.ui._dialogs['snapscreenDialog'].open()
                return
              }
              onSuccess(res)
            }, 50)
          },
          queryCommandState: function() {
            return navigator.userAgent.indexOf('Windows', 0) != -1 ? 0 : -1
          }
        }
      }
    }
  })
  UE.commands['insertparagraph'] = {
    execCommand: function(cmdName, front) {
      var me = this
      var range = me.selection.getRange()
      var start = range.startContainer
      var tmpNode
      while (start) {
        if (domUtils.isBody(start)) {
          break
        }
        tmpNode = start
        start = start.parentNode
      }
      if (tmpNode) {
        var p = me.document.createElement('p')
        if (front) {
          tmpNode.parentNode.insertBefore(p, tmpNode)
        } else {
          tmpNode.parentNode.insertBefore(p, tmpNode.nextSibling)
        }
        domUtils.fillNode(me.document, p)
        range.setStart(p, 0).setCursor(false, true)
      }
    }
  }
  UE.plugin.register('webapp', function() {
    var me = this

    function createInsertStr(obj, toEmbed) {
      return !toEmbed
        ? '<img title="' +
        obj.title +
        '" width="' +
        obj.width +
        '" height="' +
        obj.height +
        '"' +
        ' src="' +
        me.options.UEDITOR_HOME_URL +
        'themes/default/images/spacer.gif" _logo_url="' +
        obj.logo +
        '" style="background:url(' +
        obj.logo +
        ') no-repeat center center; border:1px solid gray;" class="edui-faked-webapp" _url="' +
        obj.url +
        '" ' +
        (obj.align && !obj.cssfloat ? 'align="' + obj.align + '"' : '') +
        (obj.cssfloat ? 'style="float:' + obj.cssfloat + '"' : '') +
        '/>'
        : '<iframe class="edui-faked-webapp" title="' +
        obj.title +
        '" ' +
        (obj.align && !obj.cssfloat ? 'align="' + obj.align + '"' : '') +
        (obj.cssfloat ? 'style="float:' + obj.cssfloat + '"' : '') +
        'width="' +
        obj.width +
        '" height="' +
        obj.height +
        '"  scrolling="no" frameborder="0" src="' +
        obj.url +
        '" logo_url = "' +
        obj.logo +
        '"></iframe>'
    }

    return {
      outputRule: function(root) {
        utils.each(root.getNodesByTagName('img'), function(node) {
          var html
          if (node.getAttr('class') == 'edui-faked-webapp') {
            html = createInsertStr(
              {
                title: node.getAttr('title'),
                width: node.getAttr('width'),
                height: node.getAttr('height'),
                align: node.getAttr('align'),
                cssfloat: node.getStyle('float'),
                url: node.getAttr('_url'),
                logo: node.getAttr('_logo_url')
              },
              true
            )
            var embed = UE.uNode.createElement(html)
            node.parentNode.replaceChild(embed, node)
          }
        })
      },
      inputRule: function(root) {
        utils.each(root.getNodesByTagName('iframe'), function(node) {
          if (node.getAttr('class') == 'edui-faked-webapp') {
            var img = UE.uNode.createElement(
              createInsertStr({
                title: node.getAttr('title'),
                width: node.getAttr('width'),
                height: node.getAttr('height'),
                align: node.getAttr('align'),
                cssfloat: node.getStyle('float'),
                url: node.getAttr('src'),
                logo: node.getAttr('logo_url')
              })
            )
            node.parentNode.replaceChild(img, node)
          }
        })
      },
      commands: {
        webapp: {
          execCommand: function(cmd, obj) {
            var me = this
            var str = createInsertStr(utils.extend(obj, { align: 'none' }), false)
            me.execCommand('inserthtml', str)
          },
          queryCommandState: function() {
            var me = this
            var img = me.selection.getRange().getClosedNode()
            var flag = img && img.className == 'edui-faked-webapp'
            return flag ? 1 : 0
          }
        }
      }
    }
  })
  UE.plugins['template'] = function() {
    UE.commands['template'] = {
      execCommand: function(cmd, obj) {
        obj.html && this.execCommand('inserthtml', obj.html)
      }
    }
    this.addListener('click', function(type, evt) {
      var el = evt.target || evt.srcElement
      var range = this.selection.getRange()
      var tnode = domUtils.findParent(
        el,
        function(node) {
          if (node.className && domUtils.hasClass(node, 'ue_t')) {
            return node
          }
        },
        true
      )
      tnode &&
      range
        .selectNode(tnode)
        .shrinkBoundary()
        .select()
    })
    this.addListener('keydown', function(type, evt) {
      var range = this.selection.getRange()
      if (!range.collapsed) {
        if (!evt.ctrlKey && !evt.metaKey && !evt.shiftKey && !evt.altKey) {
          var tnode = domUtils.findParent(
            range.startContainer,
            function(node) {
              if (node.className && domUtils.hasClass(node, 'ue_t')) {
                return node
              }
            },
            true
          )
          if (tnode) {
            domUtils.removeClasses(tnode, ['ue_t'])
          }
        }
      }
    })
  }
  UE.plugin.register('music', function() {
    var me = this

    function creatInsertStr(selectData, url, width, height, align, cssfloat, toEmbed) {
      return !toEmbed
        ? '<img ' +
        (align && !cssfloat ? 'align="' + align + '"' : '') +
        (cssfloat ? 'style="float:' + cssfloat + '"' : '') +
        ' width="' +
        width +
        '" height="' +
        height +
        '" _url="' +
        url +
        '" class="edui-faked-music"' +
        ' src="' +
        me.options.langPath +
        me.options.lang +
        '/images/music.png" />'
        : '<p style="color: #555;">' +
        '<p/><audio class="edui-faked-music" controls="controls" src="' +
        url +
        '" width="' +
        width +
        '" height="' +
        height +
        '" ' +
        (align && !cssfloat ? 'align="' + align + '"' : '') +
        (cssfloat ? 'style="float:' + cssfloat + '"' : '') +
        '>'
    }

    return {
      outputRule: function(root) {
        utils.each(root.getNodesByTagName('img'), function(node) {
          var html
          if (node.getAttr('class') == 'edui-faked-music') {
            var cssfloat = node.getStyle('float')
            var align = node.getAttr('align')
            html = creatInsertStr(
              node.getAttr('_url'),
              node.getAttr('width'),
              node.getAttr('height'),
              align,
              cssfloat,
              true
            )
            var embed = UE.uNode.createElement(html)
            node.parentNode.replaceChild(embed, node)
          }
        })
      },
      inputRule: function(root) {
        utils.each(root.getNodesByTagName('embed'), function(node) {
          if (node.getAttr('class') == 'edui-faked-music') {
            var cssfloat = node.getStyle('float')
            var align = node.getAttr('align')
            html = creatInsertStr(
              node.getAttr('src'),
              node.getAttr('width'),
              node.getAttr('height'),
              align,
              cssfloat,
              false
            )
            var img = UE.uNode.createElement(html)
            node.parentNode.replaceChild(img, node)
          }
        })
      },
      commands: {
        music: {
          execCommand: function(cmd, musicObj) {
            console.log(musicObj)
            var me = this
            var str = creatInsertStr(
              musicObj,
              musicObj.url,
              musicObj.width || 400,
              musicObj.height || 95,
              'none',
              false,
              true
            )
            me.execCommand('inserthtml', str)
          },
          queryCommandState: function() {
            var me = this
            var img = me.selection.getRange().getClosedNode()
            var flag = img && img.className == 'edui-faked-music'
            return flag ? 1 : 0
          }
        }
      }
    }
  })
  UE.plugin.register('autoupload', function() {
    function sendAndInsertFile(file, editor) {
      var me = editor
      var fieldName
      var urlPrefix
      var maxSize
      var allowFiles
      var actionUrl
      var loadingHtml
      var errorHandler
      var successHandler
      var filetype = /image\/\w+/i.test(file.type) ? 'image' : 'file'
      var loadingId = 'loading_' + (+new Date()).toString(36)
      fieldName = me.getOpt(filetype + 'FieldName')
      urlPrefix = me.getOpt(filetype + 'UrlPrefix')
      maxSize = me.getOpt(filetype + 'MaxSize')
      allowFiles = me.getOpt(filetype + 'AllowFiles')
      actionUrl = me.getActionUrl(me.getOpt(filetype + 'ActionName'))
      errorHandler = function(title) {
        var loader = me.document.getElementById(loadingId)
        loader && domUtils.remove(loader)
        me.fireEvent('showmessage', { id: loadingId, content: title, type: 'error', timeout: 4000 })
      }
      if (filetype == 'image') {
        loadingHtml =
          '<img class="loadingclass" id="' +
          loadingId +
          '" src="' +
          me.options.themePath +
          me.options.theme +
          '/images/spacer.gif" title="' +
          (me.getLang('autoupload.loading') || '') +
          '" >'
        successHandler = function(data) {
          var link = urlPrefix + data.url
          var loader = me.document.getElementById(loadingId)
          if (loader) {
            loader.setAttribute('src', link)
            loader.setAttribute('_src', link)
            loader.setAttribute('title', data.title || '')
            loader.setAttribute('alt', data.original || '')
            loader.removeAttribute('id')
            domUtils.removeClasses(loader, 'loadingclass')
          }
        }
      } else {
        loadingHtml =
          '<p>' +
          '<img class="loadingclass" id="' +
          loadingId +
          '" src="' +
          me.options.themePath +
          me.options.theme +
          '/images/spacer.gif" title="' +
          (me.getLang('autoupload.loading') || '') +
          '" >' +
          '</p>'
        successHandler = function(data) {
          var link = urlPrefix + data.url
          var loader = me.document.getElementById(loadingId)
          var rng = me.selection.getRange()
          var bk = rng.createBookmark()
          rng.selectNode(loader).select()
          me.execCommand('insertfile', { url: link })
          rng.moveToBookmark(bk).select()
        }
      }
      me.execCommand('inserthtml', loadingHtml)
      if (!me.getOpt(filetype + 'ActionName')) {
        errorHandler(me.getLang('autoupload.errorLoadConfig'))
        return
      }
      if (file.size > maxSize) {
        errorHandler(me.getLang('autoupload.exceedSizeError'))
        return
      }
      var fileext = file.name ? file.name.substr(file.name.lastIndexOf('.')) : ''
      if (
        (fileext && filetype != 'image') ||
        (allowFiles && (allowFiles.join('') + '.').indexOf(fileext.toLowerCase() + '.') == -1)
      ) {
        errorHandler(me.getLang('autoupload.exceedTypeError'))
        return
      }
      var xhr = new XMLHttpRequest()
      var fd = new FormData()
      var params = utils.serializeParam(me.queryCommandValue('serverparam')) || ''
      var url = utils.formatUrl(actionUrl + (actionUrl.indexOf('?') == -1 ? '?' : '&') + params)
      fd.append(fieldName, file, file.name || 'blob.' + file.type.substr('image/'.length))
      fd.append('type', 'ajax')
      xhr.open('post', url, true)
      xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest')
      xhr.addEventListener('load', function(e) {
        try {
          var json = new Function('return ' + utils.trim(e.target.response))()
          if (json.state == 'SUCCESS' && json.url) {
            successHandler(json)
          } else {
            errorHandler(json.state)
          }
        } catch (er) {
          errorHandler(me.getLang('autoupload.loadError'))
        }
      })
      xhr.send(fd)
    }

    function getPasteImage(e) {
      return e.clipboardData &&
      e.clipboardData.items &&
      e.clipboardData.items.length == 1 &&
      /^image\//.test(e.clipboardData.items[0].type)
        ? e.clipboardData.items
        : null
    }

    function getDropImage(e) {
      return e.dataTransfer && e.dataTransfer.files ? e.dataTransfer.files : null
    }

    return {
      outputRule: function(root) {
        utils.each(root.getNodesByTagName('img'), function(n) {
          if (/\b(loaderrorclass)|(bloaderrorclass)\b/.test(n.getAttr('class'))) {
            n.parentNode.removeChild(n)
          }
        })
        utils.each(root.getNodesByTagName('p'), function(n) {
          if (/\bloadpara\b/.test(n.getAttr('class'))) {
            n.parentNode.removeChild(n)
          }
        })
      },
      bindEvents: {
        ready: function(e) {
          var me = this
          if (window.FormData && window.FileReader) {
            domUtils.on(me.body, 'paste drop', function(e) {
              var hasImg = false
              var items
              items = e.type == 'paste' ? getPasteImage(e) : getDropImage(e)
              if (items) {
                var len = items.length
                var file
                while (len--) {
                  file = items[len]
                  if (file.getAsFile) {
                    file = file.getAsFile()
                  }
                  if (file && file.size > 0) {
                    sendAndInsertFile(file, me)
                    hasImg = true
                  }
                }
                hasImg && e.preventDefault()
              }
            })
            domUtils.on(me.body, 'dragover', function(e) {
              if (e.dataTransfer.types[0] == 'Files') {
                e.preventDefault()
              }
            })
            utils.cssRule(
              'loading',
              '.loadingclass{display:inline-block;cursor:default;background: url(\'' +
              this.options.themePath +
              this.options.theme +
              '/images/loading.gif\') no-repeat center center transparent;border:1px solid #cccccc;margin-left:1px;height: 22px;width: 22px;}\n' +
              '.loaderrorclass{display:inline-block;cursor:default;background: url(\'' +
              this.options.themePath +
              this.options.theme +
              '/images/loaderror.png\') no-repeat center center transparent;border:1px solid #cccccc;margin-right:1px;height: 22px;width: 22px;' +
              '}',
              this.document
            )
          }
        }
      }
    }
  })
  UE.plugin.register('autosave', function() {
    var me = this
    var lastSaveTime = new Date()
    var MIN_TIME = 20
    var saveKey = null

    function save(editor) {
      var saveData
      if (new Date() - lastSaveTime < MIN_TIME) {
        return
      }
      if (!editor.hasContents()) {
        saveKey && me.removePreferences(saveKey)
        return
      }
      lastSaveTime = new Date()
      editor._saveFlag = null
      saveData = me.body.innerHTML
      if (editor.fireEvent('beforeautosave', { content: saveData }) === false) {
        return
      }
      me.setPreferences(saveKey, saveData)
      editor.fireEvent('afterautosave', { content: saveData })
    }

    return {
      defaultOptions: { saveInterval: 500 },
      bindEvents: {
        ready: function() {
          var _suffix = '-drafts-data'
          var key = null
          if (me.key) {
            key = me.key + _suffix
          } else {
            key = (me.container.parentNode.id || 'ue-common') + _suffix
          }
          saveKey = (location.protocol + location.host + location.pathname).replace(/[.:\/]/g, '_') + key
        },
        contentchange: function() {
          if (!saveKey) {
            return
          }
          if (me._saveFlag) {
            window.clearTimeout(me._saveFlag)
          }
          if (me.options.saveInterval > 0) {
            me._saveFlag = window.setTimeout(function() {
              save(me)
            }, me.options.saveInterval)
          } else {
            save(me)
          }
        }
      },
      commands: {
        clearlocaldata: {
          execCommand: function(cmd, name) {
            if (saveKey && me.getPreferences(saveKey)) {
              me.removePreferences(saveKey)
            }
          },
          notNeedUndo: true,
          ignoreContentChange: true
        },
        getlocaldata: {
          execCommand: function(cmd, name) {
            return saveKey ? me.getPreferences(saveKey) || '' : ''
          },
          notNeedUndo: true,
          ignoreContentChange: true
        },
        drafts: {
          execCommand: function(cmd, name) {
            if (saveKey) {
              me.body.innerHTML = me.getPreferences(saveKey) || '<p>' + domUtils.fillHtml + '</p>'
              me.focus(true)
            }
          },
          queryCommandState: function() {
            return saveKey ? (me.getPreferences(saveKey) === null ? -1 : 0) : -1
          },
          notNeedUndo: true,
          ignoreContentChange: true
        }
      }
    }
  })
  UE.plugin.register('charts', function() {
    var me = this
    return {
      bindEvents: {
        chartserror: function() {
        }
      },
      commands: {
        charts: {
          execCommand: function(cmd, data) {
            var tableNode = domUtils.findParentByTagName(this.selection.getRange().startContainer, 'table', true)
            var flagText = []
            var config = {}
            if (!tableNode) {
              return false
            }
            if (!validData(tableNode)) {
              me.fireEvent('chartserror')
              return false
            }
            config.title = data.title || ''
            config.subTitle = data.subTitle || ''
            config.xTitle = data.xTitle || ''
            config.yTitle = data.yTitle || ''
            config.suffix = data.suffix || ''
            config.tip = data.tip || ''
            config.dataFormat = data.tableDataFormat || ''
            config.chartType = data.chartType || 0
            for (var key in config) {
              if (!config.hasOwnProperty(key)) {
                continue
              }
              flagText.push(key + ':' + config[key])
            }
            tableNode.setAttribute('data-chart', flagText.join(';'))
            domUtils.addClass(tableNode, 'edui-charts-table')
          },
          queryCommandState: function(cmd, name) {
            var tableNode = domUtils.findParentByTagName(this.selection.getRange().startContainer, 'table', true)
            return tableNode && validData(tableNode) ? 0 : -1
          }
        }
      },
      inputRule: function(root) {
        utils.each(root.getNodesByTagName('table'), function(tableNode) {
          if (tableNode.getAttr('data-chart') !== undefined) {
            tableNode.setAttr('style')
          }
        })
      },
      outputRule: function(root) {
        utils.each(root.getNodesByTagName('table'), function(tableNode) {
          if (tableNode.getAttr('data-chart') !== undefined) {
            tableNode.setAttr('style', 'display: none;')
          }
        })
      }
    }

    function validData(table) {
      var firstRows = null
      var cellCount = 0
      if (table.rows.length < 2) {
        return false
      }
      if (table.rows[0].cells.length < 2) {
        return false
      }
      firstRows = table.rows[0].cells
      cellCount = firstRows.length
      for (var i = 0, cell; (cell = firstRows[i]); i++) {
        if (cell.tagName.toLowerCase() !== 'th') {
          return false
        }
      }
      for (var i = 1, row; (row = table.rows[i]); i++) {
        if (row.cells.length != cellCount) {
          return false
        }
        if (row.cells[0].tagName.toLowerCase() !== 'th') {
          return false
        }
        for (var j = 1, cell; (cell = row.cells[j]); j++) {
          var value = utils.trim(cell.innerText || cell.textContent || '')
          value = value.replace(new RegExp(UE.dom.domUtils.fillChar, 'g'), '').replace(/^\s+|\s+$/g, '')
          if (!/^\d*\.?\d+$/.test(value)) {
            return false
          }
        }
      }
      return true
    }
  })
  UE.plugin.register('section', function() {
    function Section(option) {
      this.tag = ''
      ;(this.level = -1), (this.dom = null)
      this.nextSection = null
      this.previousSection = null
      this.parentSection = null
      this.startAddress = []
      this.endAddress = []
      this.children = []
    }

    function getSection(option) {
      var section = new Section()
      return utils.extend(section, option)
    }

    function getNodeFromAddress(startAddress, root) {
      var current = root
      for (var i = 0; i < startAddress.length; i++) {
        if (!current.childNodes) {
          return null
        }
        current = current.childNodes[startAddress[i]]
      }
      return current
    }

    var me = this
    return {
      bindMultiEvents: {
        type: 'aftersetcontent afterscencerestore',
        handler: function() {
          me.fireEvent('updateSections')
        }
      },
      bindEvents: {
        ready: function() {
          me.fireEvent('updateSections')
          domUtils.on(me.body, 'drop paste', function() {
            me.fireEvent('updateSections')
          })
        },
        afterexeccommand: function(type, cmd) {
          if (cmd == 'paragraph') {
            me.fireEvent('updateSections')
          }
        },
        keyup: function(type, e) {
          var me = this
          var range = me.selection.getRange()
          if (range.collapsed != true) {
            me.fireEvent('updateSections')
          } else {
            var keyCode = e.keyCode || e.which
            if (keyCode == 13 || keyCode == 8 || keyCode == 46) {
              me.fireEvent('updateSections')
            }
          }
        }
      },
      commands: {
        getsections: {
          execCommand: function(cmd, levels) {
            var levelFn = levels || ['h1', 'h2', 'h3', 'h4', 'h5', 'h6']
            for (var i = 0; i < levelFn.length; i++) {
              if (typeof levelFn[i] === 'string') {
                levelFn[i] = (function(fn) {
                  return function(node) {
                    return node.tagName == fn.toUpperCase()
                  }
                })(levelFn[i])
              } else {
                if (typeof levelFn[i] !== 'function') {
                  levelFn[i] = function(node) {
                    return null
                  }
                }
              }
            }

            function getSectionLevel(node) {
              for (var i = 0; i < levelFn.length; i++) {
                if (levelFn[i](node)) {
                  return i
                }
              }
              return -1
            }

            var me = this
            var Directory = getSection({ level: -1, title: 'root' })
            var previous = Directory

            function traversal(node, Directory) {
              var level
              var tmpSection = null
              var parent
              var child
              var children = node.childNodes
              for (var i = 0, len = children.length; i < len; i++) {
                child = children[i]
                level = getSectionLevel(child)
                if (level >= 0) {
                  var address = me.selection
                    .getRange()
                    .selectNode(child)
                    .createAddress(true).startAddress
                  var current = getSection({
                    tag: child.tagName,
                    title: child.innerText || child.textContent || '',
                    level: level,
                    dom: child,
                    startAddress: utils.clone(address, []),
                    endAddress: utils.clone(address, []),
                    children: []
                  })
                  previous.nextSection = current
                  current.previousSection = previous
                  parent = previous
                  while (level <= parent.level) {
                    parent = parent.parentSection
                  }
                  current.parentSection = parent
                  parent.children.push(current)
                  tmpSection = previous = current
                } else {
                  child.nodeType === 1 && traversal(child, Directory)
                  tmpSection && tmpSection.endAddress[tmpSection.endAddress.length - 1]++
                }
              }
            }

            traversal(me.body, Directory)
            return Directory
          },
          notNeedUndo: true
        },
        movesection: {
          execCommand: function(cmd, sourceSection, targetSection, isAfter) {
            var me = this
            var targetAddress
            var target
            if (!sourceSection || !targetSection || targetSection.level == -1) {
              return
            }
            targetAddress = isAfter ? targetSection.endAddress : targetSection.startAddress
            target = getNodeFromAddress(targetAddress, me.body)
            if (
              !targetAddress ||
              !target ||
              isContainsAddress(sourceSection.startAddress, sourceSection.endAddress, targetAddress)
            ) {
              return
            }
            var startNode = getNodeFromAddress(sourceSection.startAddress, me.body)
            var endNode = getNodeFromAddress(sourceSection.endAddress, me.body)
            var current
            var nextNode
            if (isAfter) {
              current = endNode
              while (current && !(domUtils.getPosition(startNode, current) & domUtils.POSITION_FOLLOWING)) {
                nextNode = current.previousSibling
                domUtils.insertAfter(target, current)
                if (current == startNode) {
                  break
                }
                current = nextNode
              }
            } else {
              current = startNode
              while (current && !(domUtils.getPosition(current, endNode) & domUtils.POSITION_FOLLOWING)) {
                nextNode = current.nextSibling
                target.parentNode.insertBefore(current, target)
                if (current == endNode) {
                  break
                }
                current = nextNode
              }
            }
            me.fireEvent('updateSections')

            function isContainsAddress(startAddress, endAddress, addressTarget) {
              var isAfterStartAddress = false
              var isBeforeEndAddress = false
              for (var i = 0; i < startAddress.length; i++) {
                if (i >= addressTarget.length) {
                  break
                }
                if (addressTarget[i] > startAddress[i]) {
                  isAfterStartAddress = true
                  break
                } else {
                  if (addressTarget[i] < startAddress[i]) {
                    break
                  }
                }
              }
              for (var i = 0; i < endAddress.length; i++) {
                if (i >= addressTarget.length) {
                  break
                }
                if (addressTarget[i] < startAddress[i]) {
                  isBeforeEndAddress = true
                  break
                } else {
                  if (addressTarget[i] > startAddress[i]) {
                    break
                  }
                }
              }
              return isAfterStartAddress && isBeforeEndAddress
            }
          }
        },
        deletesection: {
          execCommand: function(cmd, section, keepChildren) {
            var me = this
            if (!section) {
              return
            }

            function getNodeFromAddress(startAddress) {
              var current = me.body
              for (var i = 0; i < startAddress.length; i++) {
                if (!current.childNodes) {
                  return null
                }
                current = current.childNodes[startAddress[i]]
              }
              return current
            }

            var startNode = getNodeFromAddress(section.startAddress)
            var endNode = getNodeFromAddress(section.endAddress)
            var current = startNode
            var nextNode
            if (!keepChildren) {
              while (
                current &&
                domUtils.inDoc(endNode, me.document) &&
                !(domUtils.getPosition(current, endNode) & domUtils.POSITION_FOLLOWING)
                ) {
                nextNode = current.nextSibling
                domUtils.remove(current)
                current = nextNode
              }
            } else {
              domUtils.remove(current)
            }
            me.fireEvent('updateSections')
          }
        },
        selectsection: {
          execCommand: function(cmd, section) {
            if (!section && !section.dom) {
              return false
            }
            var me = this
            var range = me.selection.getRange()
            var address = {
              startAddress: utils.clone(section.startAddress, []),
              endAddress: utils.clone(section.endAddress, [])
            }
            address.endAddress[address.endAddress.length - 1]++
            range
              .moveToAddress(address)
              .select()
              .scrollToView()
            return true
          },
          notNeedUndo: true
        },
        scrolltosection: {
          execCommand: function(cmd, section) {
            if (!section && !section.dom) {
              return false
            }
            var me = this
            var range = me.selection.getRange()
            var address = { startAddress: section.startAddress, endAddress: section.endAddress }
            address.endAddress[address.endAddress.length - 1]++
            range.moveToAddress(address).scrollToView()
            return true
          },
          notNeedUndo: true
        }
      }
    }
  })
  UE.plugin.register('simpleupload', function() {
    var me = this
    var isLoaded = false
    var containerBtn

    function initUploadBtn() {
      var w = containerBtn.offsetWidth || 20
      var h = containerBtn.offsetHeight || 20
      var btnIframe = document.createElement('iframe')
      var btnStyle =
        'display:block;width:' +
        w +
        'px;height:' +
        h +
        'px;overflow:hidden;border:0;margin:0;padding:0;position:absolute;top:0;left:0;filter:alpha(opacity=0);-moz-opacity:0;-khtml-opacity: 0;opacity: 0;cursor:pointer;'
      domUtils.on(btnIframe, 'load', function() {
        var timestrap = (+new Date()).toString(36)
        var wrapper
        var btnIframeDoc
        var btnIframeBody
        btnIframeDoc = btnIframe.contentDocument || btnIframe.contentWindow.document
        btnIframeBody = btnIframeDoc.body
        wrapper = btnIframeDoc.createElement('div')
        wrapper.innerHTML =
          '<form id="edui_form_' +
          timestrap +
          '" target="edui_iframe_' +
          timestrap +
          '" method="POST" enctype="multipart/form-data" action="' +
          me.getOpt('serverUrl') +
          '" ' +
          'style="' +
          btnStyle +
          '">' +
          '<input id="edui_input_' +
          timestrap +
          '" type="file" accept="image/*" name="' +
          me.options.imageFieldName +
          '" ' +
          'style="' +
          btnStyle +
          '">' +
          '<input id="edui_input_hidden" type="hidden" value="111" > ' +
          '</form>' +
          '<iframe id="edui_iframe_' +
          timestrap +
          '" name="edui_iframe_' +
          timestrap +
          '" style="display:none;width:0;height:0;border:0;margin:0;padding:0;position:absolute;"></iframe>'
        wrapper.className = 'edui-' + me.options.theme
        wrapper.id = me.ui.id + '_iframeupload'
        btnIframeBody.style.cssText = btnStyle
        btnIframeBody.style.width = w + 'px'
        btnIframeBody.style.height = h + 'px'
        btnIframeBody.appendChild(wrapper)
        if (btnIframeBody.parentNode) {
          btnIframeBody.parentNode.style.width = w + 'px'
          btnIframeBody.parentNode.style.height = w + 'px'
        }
        var form = btnIframeDoc.getElementById('edui_form_' + timestrap)
        var input = btnIframeDoc.getElementById('edui_input_' + timestrap)
        var iframe = btnIframeDoc.getElementById('edui_iframe_' + timestrap)
        domUtils.on(input, 'change', function() {
          if (!input.value) {
            return
          }
          var loadingId = 'loading_' + (+new Date()).toString(36)
          var params = utils.serializeParam(me.queryCommandValue('serverparam')) || ''
          var imageActionUrl = me.getActionUrl(me.getOpt('imageActionName'))
          var allowFiles = me.getOpt('imageAllowFiles')
          me.focus()
          me.execCommand(
            'inserthtml',
            '<img class="loadingclass" id="' +
            loadingId +
            '" src="' +
            me.options.themePath +
            me.options.theme +
            '/images/spacer.gif" title="' +
            (me.getLang('simpleupload.loading') || '') +
            '" >'
          )

          function callback() {
            try {
              var link
              var json
              var loader
              var body = (iframe.contentDocument || iframe.contentWindow.document).body
              var result = body.innerText || body.textContent || ''
              json = new Function('return ' + result)()
              link = me.options.imageUrlPrefix + json.url
              if (json.state == 'SUCCESS' && json.url) {
                loader = me.document.getElementById(loadingId)
                loader.setAttribute('src', link)
                loader.setAttribute('_src', link)
                loader.setAttribute('title', json.title || '')
                loader.setAttribute('alt', json.original || '')
                loader.removeAttribute('id')
                domUtils.removeClasses(loader, 'loadingclass')
              } else {
                showErrorLoader && showErrorLoader(json.state)
              }
            } catch (er) {
              showErrorLoader && showErrorLoader(me.getLang('simpleupload.loadError'))
            }
            form.reset()
            domUtils.un(iframe, 'load', callback)
          }

          function showErrorLoader(title) {
            if (loadingId) {
              var loader = me.document.getElementById(loadingId)
              loader && domUtils.remove(loader)
              me.fireEvent('showmessage', { id: loadingId, content: title, type: 'error', timeout: 4000 })
            }
          }

          if (!me.getOpt('imageActionName')) {
            errorHandler(me.getLang('autoupload.errorLoadConfig'))
            return
          }
          var filename = input.value
          var fileext = filename ? filename.substr(filename.lastIndexOf('.')) : ''
          if (!fileext || (allowFiles && (allowFiles.join('') + '.').indexOf(fileext.toLowerCase() + '.') == -1)) {
            showErrorLoader(me.getLang('simpleupload.exceedTypeError'))
            return
          }
          domUtils.on(iframe, 'load', callback)
          form.action = utils.formatUrl(imageActionUrl + (imageActionUrl.indexOf('?') == -1 ? '?' : '&') + params)
          form.submit()
        })
        var stateTimer
        me.addListener('selectionchange', function() {
          clearTimeout(stateTimer)
          stateTimer = setTimeout(function() {
            var state = me.queryCommandState('simpleupload')
            if (state == -1) {
              input.disabled = 'disabled'
            } else {
              input.disabled = false
            }
          }, 400)
        })
        isLoaded = true
      })
      btnIframe.style.cssText = btnStyle
      containerBtn.appendChild(btnIframe)
    }

    return {
      bindEvents: {
        ready: function() {
          utils.cssRule(
            'loading',
            '.loadingclass{display:inline-block;cursor:default;background: url(\'' +
            this.options.themePath +
            this.options.theme +
            '/images/loading.gif\') no-repeat center center transparent;border:1px solid #cccccc;margin-right:1px;height: 22px;width: 22px;}\n' +
            '.loaderrorclass{display:inline-block;cursor:default;background: url(\'' +
            this.options.themePath +
            this.options.theme +
            '/images/loaderror.png\') no-repeat center center transparent;border:1px solid #cccccc;margin-right:1px;height: 22px;width: 22px;' +
            '}',
            this.document
          )
        },
        simpleuploadbtnready: function(type, container) {
          containerBtn = container
          me.afterConfigReady(initUploadBtn)
        }
      },
      outputRule: function(root) {
        utils.each(root.getNodesByTagName('img'), function(n) {
          if (/\b(loaderrorclass)|(bloaderrorclass)\b/.test(n.getAttr('class'))) {
            n.parentNode.removeChild(n)
          }
        })
      },
      commands: {
        simpleupload: {
          queryCommandState: function() {
            return isLoaded ? 0 : -1
          }
        }
      }
    }
  })
  UE.plugin.register('serverparam', function() {
    var me = this
    var serverParam = {}
    return {
      commands: {
        serverparam: {
          execCommand: function(cmd, key, value) {
            if (key === undefined || key === null) {
              serverParam = {}
            } else {
              if (utils.isString(key)) {
                if (value === undefined || value === null) {
                  delete serverParam[key]
                } else {
                  serverParam[key] = value
                }
              } else {
                if (utils.isObject(key)) {
                  utils.extend(serverParam, key, true)
                } else {
                  if (utils.isFunction(key)) {
                    utils.extend(serverParam, key(), true)
                  }
                }
              }
            }
          },
          queryCommandValue: function() {
            return serverParam || {}
          }
        }
      }
    }
  })
  UE.plugin.register('insertfile', function() {
    var me = this

    function getFileIcon(url) {
      var ext = url.substr(url.lastIndexOf('.') + 1).toLowerCase()
      var maps = {
        rar: 'icon_rar.gif',
        zip: 'icon_rar.gif',
        tar: 'icon_rar.gif',
        gz: 'icon_rar.gif',
        bz2: 'icon_rar.gif',
        doc: 'icon_doc.gif',
        docx: 'icon_doc.gif',
        pdf: 'icon_pdf.gif',
        mp3: 'icon_mp3.gif',
        xls: 'icon_xls.gif',
        chm: 'icon_chm.gif',
        ppt: 'icon_ppt.gif',
        pptx: 'icon_ppt.gif',
        avi: 'icon_mv.gif',
        rmvb: 'icon_mv.gif',
        wmv: 'icon_mv.gif',
        flv: 'icon_mv.gif',
        swf: 'icon_mv.gif',
        rm: 'icon_mv.gif',
        exe: 'icon_exe.gif',
        psd: 'icon_psd.gif',
        txt: 'icon_txt.gif',
        jpg: 'icon_jpg.gif',
        png: 'icon_jpg.gif',
        jpeg: 'icon_jpg.gif',
        gif: 'icon_jpg.gif',
        ico: 'icon_jpg.gif',
        bmp: 'icon_jpg.gif'
      }
      return maps[ext] ? maps[ext] : maps['txt']
    }

    return {
      commands: {
        insertfile: {
          execCommand: function(command, filelist) {
            filelist = utils.isArray(filelist) ? filelist : [filelist]
            var i
            var item
            var icon
            var title
            var html = ''
            var URL = me.getOpt('UEDITOR_HOME_URL')
            var iconDir = URL + (URL.substr(URL.length - 1) == '/' ? '' : '/') + 'dialogs/attachment/fileTypeImages/'
            for (i = 0; i < filelist.length; i++) {
              item = filelist[i]
              icon = iconDir + getFileIcon(item.url)
              title = item.title || item.url.substr(item.url.lastIndexOf('/') + 1)
              html +=
                '<p style="line-height: 18px;font-size:18px">' +
                '<img style="vertical-align: middle; margin-right: 2px;" src="' +
                icon +
                '" _src="' +
                icon +
                '" />' +
                '<a style="font-size:12px; color:#0066cc;" href="' +
                item.url +
                '" title="' +
                title +
                '">' +
                title +
                '</a>' +
                '</p>'
            }
            me.execCommand('insertHtml', html)
          }
        }
      }
    }
  })
  UE.plugins.xssFilter = function() {
    var config = UEDITOR_CONFIG
    var whitList = config.whitList

    function filter(node) {
      var tagName = node.tagName
      var attrs = node.attrs
      if (!whitList.hasOwnProperty(tagName)) {
        node.parentNode.removeChild(node)
        return false
      }
      UE.utils.each(attrs, function(val, key) {
        if (whitList[tagName].indexOf(key) === -1) {
          node.setAttr(key)
        }
      })
    }

    if (whitList && config.xssFilterRules) {
      this.options.filterRules = (function() {
        var result = {}
        UE.utils.each(whitList, function(val, key) {
          result[key] = function(node) {
            return filter(node)
          }
        })
        return result
      })()
    }
    var tagList = []
    UE.utils.each(whitList, function(val, key) {
      tagList.push(key)
    })
    if (whitList && config.inputXssFilter) {
      this.addInputRule(function(root) {
        root.traversal(function(node) {
          if (node.type !== 'element') {
            return false
          }
          filter(node)
        })
      })
    }
    if (whitList && config.outputXssFilter) {
      this.addOutputRule(function(root) {
        root.traversal(function(node) {
          if (node.type !== 'element') {
            return false
          }
          filter(node)
        })
      })
    }
  }
  var baidu = baidu || {}
  baidu.editor = baidu.editor || {}
  UE.ui = baidu.editor.ui = {}
  ;(function() {
    var browser = baidu.editor.browser
    var domUtils = baidu.editor.dom.domUtils
    var magic = '$EDITORUI'
    var root = (window[magic] = {})
    var uidMagic = 'ID' + magic
    var uidCount = 0
    var uiUtils = (baidu.editor.ui.uiUtils = {
      uid: function(obj) {
        return obj ? obj[uidMagic] || (obj[uidMagic] = ++uidCount) : ++uidCount
      },
      hook: function(fn, callback) {
        var dg
        if (fn && fn._callbacks) {
          dg = fn
        } else {
          dg = function() {
            var q
            if (fn) {
              q = fn.apply(this, arguments)
            }
            var callbacks = dg._callbacks
            var k = callbacks.length
            while (k--) {
              var r = callbacks[k].apply(this, arguments)
              if (q === undefined) {
                q = r
              }
            }
            return q
          }
          dg._callbacks = []
        }
        dg._callbacks.push(callback)
        return dg
      },
      createElementByHtml: function(html) {
        var el = document.createElement('div')
        el.innerHTML = html
        el = el.firstChild
        el.parentNode.removeChild(el)
        return el
      },
      getViewportElement: function() {
        return browser.ie && browser.quirks ? document.body : document.documentElement
      },
      getClientRect: function(element) {
        var bcr
        try {
          bcr = element.getBoundingClientRect()
        } catch (e) {
          bcr = { left: 0, top: 0, height: 0, width: 0 }
        }
        var rect = {
          left: Math.round(bcr.left),
          top: Math.round(bcr.top),
          height: Math.round(bcr.bottom - bcr.top),
          width: Math.round(bcr.right - bcr.left)
        }
        var doc
        while ((doc = element.ownerDocument) !== document && (element = domUtils.getWindow(doc).frameElement)) {
          bcr = element.getBoundingClientRect()
          rect.left += bcr.left
          rect.top += bcr.top
        }
        rect.bottom = rect.top + rect.height
        rect.right = rect.left + rect.width
        return rect
      },
      getViewportRect: function() {
        var viewportEl = uiUtils.getViewportElement()
        var width = (window.innerWidth || viewportEl.clientWidth) | 0
        var height = (window.innerHeight || viewportEl.clientHeight) | 0
        return { left: 0, top: 0, height: height, width: width, bottom: height, right: width }
      },
      setViewportOffset: function(element, offset) {
        var rect
        var fixedLayer = uiUtils.getFixedLayer()
        if (element.parentNode === fixedLayer) {
          element.style.left = offset.left + 'px'
          element.style.top = offset.top + 'px'
        } else {
          domUtils.setViewportOffset(element, offset)
        }
      },
      getEventOffset: function(evt) {
        var el = evt.target || evt.srcElement
        var rect = uiUtils.getClientRect(el)
        var offset = uiUtils.getViewportOffsetByEvent(evt)
        return { left: offset.left - rect.left, top: offset.top - rect.top }
      },
      getViewportOffsetByEvent: function(evt) {
        var el = evt.target || evt.srcElement
        var frameEl = domUtils.getWindow(el).frameElement
        var offset = { left: evt.clientX, top: evt.clientY }
        if (frameEl && el.ownerDocument !== document) {
          var rect = uiUtils.getClientRect(frameEl)
          offset.left += rect.left
          offset.top += rect.top
        }
        return offset
      },
      setGlobal: function(id, obj) {
        root[id] = obj
        return magic + '["' + id + '"]'
      },
      unsetGlobal: function(id) {
        delete root[id]
      },
      copyAttributes: function(tgt, src) {
        var attributes = src.attributes
        var k = attributes.length
        while (k--) {
          var attrNode = attributes[k]
          if (attrNode.nodeName != 'style' && attrNode.nodeName != 'class' && (!browser.ie || attrNode.specified)) {
            tgt.setAttribute(attrNode.nodeName, attrNode.nodeValue)
          }
        }
        if (src.className) {
          domUtils.addClass(tgt, src.className)
        }
        if (src.style.cssText) {
          tgt.style.cssText += ';' + src.style.cssText
        }
      },
      removeStyle: function(el, styleName) {
        if (el.style.removeProperty) {
          el.style.removeProperty(styleName)
        } else {
          if (el.style.removeAttribute) {
            el.style.removeAttribute(styleName)
          } else {
            throw ''
          }
        }
      },
      contains: function(elA, elB) {
        return (
          elA && elB && (elA === elB ? false : elA.contains ? elA.contains(elB) : elA.compareDocumentPosition(elB) & 16)
        )
      },
      startDrag: function(evt, callbacks, doc) {
        var doc = doc || document
        var startX = evt.clientX
        var startY = evt.clientY

        function handleMouseMove(evt) {
          var x = evt.clientX - startX
          var y = evt.clientY - startY
          callbacks.ondragmove(x, y, evt)
          if (evt.stopPropagation) {
            evt.stopPropagation()
          } else {
            evt.cancelBubble = true
          }
        }

        if (doc.addEventListener) {
          function handleMouseUp(evt) {
            doc.removeEventListener('mousemove', handleMouseMove, true)
            doc.removeEventListener('mouseup', handleMouseUp, true)
            window.removeEventListener('mouseup', handleMouseUp, true)
            callbacks.ondragstop()
          }

          doc.addEventListener('mousemove', handleMouseMove, true)
          doc.addEventListener('mouseup', handleMouseUp, true)
          window.addEventListener('mouseup', handleMouseUp, true)
          evt.preventDefault()
        } else {
          var elm = evt.srcElement
          elm.setCapture()

          function releaseCaptrue() {
            elm.releaseCapture()
            elm.detachEvent('onmousemove', handleMouseMove)
            elm.detachEvent('onmouseup', releaseCaptrue)
            elm.detachEvent('onlosecaptrue', releaseCaptrue)
            callbacks.ondragstop()
          }

          elm.attachEvent('onmousemove', handleMouseMove)
          elm.attachEvent('onmouseup', releaseCaptrue)
          elm.attachEvent('onlosecaptrue', releaseCaptrue)
          evt.returnValue = false
        }
        callbacks.ondragstart()
      },
      getFixedLayer: function() {
        var layer = document.getElementById('edui_fixedlayer')
        if (layer == null) {
          layer = document.createElement('div')
          layer.id = 'edui_fixedlayer'
          document.body.appendChild(layer)
          if (browser.ie && browser.version <= 8) {
            layer.style.position = 'absolute'
            bindFixedLayer()
            setTimeout(updateFixedOffset)
          } else {
            layer.style.position = 'fixed'
          }
          layer.style.left = '0'
          layer.style.top = '0'
          layer.style.width = '0'
          layer.style.height = '0'
        }
        return layer
      },
      makeUnselectable: function(element) {
        if (browser.opera || (browser.ie && browser.version < 9)) {
          element.unselectable = 'on'
          if (element.hasChildNodes()) {
            for (var i = 0; i < element.childNodes.length; i++) {
              if (element.childNodes[i].nodeType == 1) {
                uiUtils.makeUnselectable(element.childNodes[i])
              }
            }
          }
        } else {
          if (element.style.MozUserSelect !== undefined) {
            element.style.MozUserSelect = 'none'
          } else {
            if (element.style.WebkitUserSelect !== undefined) {
              element.style.WebkitUserSelect = 'none'
            } else {
              if (element.style.KhtmlUserSelect !== undefined) {
                element.style.KhtmlUserSelect = 'none'
              }
            }
          }
        }
      }
    })

    function updateFixedOffset() {
      var layer = document.getElementById('edui_fixedlayer')
      uiUtils.setViewportOffset(layer, { left: 0, top: 0 })
    }

    function bindFixedLayer(adjOffset) {
      domUtils.on(window, 'scroll', updateFixedOffset)
      domUtils.on(window, 'resize', baidu.editor.utils.defer(updateFixedOffset, 0, true))
    }
  })()
  ;(function() {
    var utils = baidu.editor.utils
    var uiUtils = baidu.editor.ui.uiUtils
    var EventBase = baidu.editor.EventBase
    var UIBase = (baidu.editor.ui.UIBase = function() {
    })
    UIBase.prototype = {
      className: '',
      uiName: '',
      initOptions: function(options) {
        var me = this
        for (var k in options) {
          me[k] = options[k]
        }
        this.id = this.id || 'edui' + uiUtils.uid()
      },
      initUIBase: function() {
        this._globalKey = utils.unhtml(uiUtils.setGlobal(this.id, this))
      },
      render: function(holder) {
        var html = this.renderHtml()
        var el = uiUtils.createElementByHtml(html)
        var list = domUtils.getElementsByTagName(el, '*')
        var theme = 'edui-' + (this.theme || this.editor.options.theme)
        var layer = document.getElementById('edui_fixedlayer')
        for (var i = 0, node; (node = list[i++]);) {
          domUtils.addClass(node, theme)
        }
        domUtils.addClass(el, theme)
        if (layer) {
          layer.className = ''
          domUtils.addClass(layer, theme)
        }
        var seatEl = this.getDom()
        if (seatEl != null) {
          seatEl.parentNode.replaceChild(el, seatEl)
          uiUtils.copyAttributes(el, seatEl)
        } else {
          if (typeof holder === 'string') {
            holder = document.getElementById(holder)
          }
          holder = holder || uiUtils.getFixedLayer()
          domUtils.addClass(holder, theme)
          holder.appendChild(el)
        }
        this.postRender()
      },
      getDom: function(name) {
        if (!name) {
          return document.getElementById(this.id)
        } else {
          return document.getElementById(this.id + '_' + name)
        }
      },
      postRender: function() {
        this.fireEvent('postrender')
      },
      getHtmlTpl: function() {
        return ''
      },
      formatHtml: function(tpl) {
        var prefix = 'edui-' + this.uiName
        return tpl
          .replace(/##/g, this.id)
          .replace(/%%-/g, this.uiName ? prefix + '-' : '')
          .replace(/%%/g, (this.uiName ? prefix : '') + ' ' + this.className)
          .replace(/\$\$/g, this._globalKey)
      },
      renderHtml: function() {
        return this.formatHtml(this.getHtmlTpl())
      },
      dispose: function() {
        var box = this.getDom()
        if (box) {
          baidu.editor.dom.domUtils.remove(box)
        }
        uiUtils.unsetGlobal(this.id)
      }
    }
    utils.inherits(UIBase, EventBase)
  })()
  ;(function() {
    var utils = baidu.editor.utils
    var UIBase = baidu.editor.ui.UIBase
    var Separator = (baidu.editor.ui.Separator = function(options) {
      this.initOptions(options)
      this.initSeparator()
    })
    Separator.prototype = {
      uiName: 'separator',
      initSeparator: function() {
        this.initUIBase()
      },
      getHtmlTpl: function() {
        return '<div id="##" class="edui-box %%"></div>'
      }
    }
    utils.inherits(Separator, UIBase)
  })()
  ;(function() {
    var utils = baidu.editor.utils
    var domUtils = baidu.editor.dom.domUtils
    var UIBase = baidu.editor.ui.UIBase
    var uiUtils = baidu.editor.ui.uiUtils
    var Mask = (baidu.editor.ui.Mask = function(options) {
      this.initOptions(options)
      this.initUIBase()
    })
    Mask.prototype = {
      getHtmlTpl: function() {
        return '<div id="##" class="edui-mask %%" onclick="return $$._onClick(event, this);" onmousedown="return $$._onMouseDown(event, this);"></div>'
      },
      postRender: function() {
        var me = this
        domUtils.on(window, 'resize', function() {
          setTimeout(function() {
            if (!me.isHidden()) {
              me._fill()
            }
          })
        })
      },
      show: function(zIndex) {
        this._fill()
        this.getDom().style.display = ''
        this.getDom().style.zIndex = zIndex
      },
      hide: function() {
        this.getDom().style.display = 'none'
        this.getDom().style.zIndex = ''
      },
      isHidden: function() {
        return this.getDom().style.display == 'none'
      },
      _onMouseDown: function() {
        return false
      },
      _onClick: function(e, target) {
        this.fireEvent('click', e, target)
      },
      _fill: function() {
        var el = this.getDom()
        var vpRect = uiUtils.getViewportRect()
        el.style.width = vpRect.width + 'px'
        el.style.height = vpRect.height + 'px'
      }
    }
    utils.inherits(Mask, UIBase)
  })()
  ;(function() {
    var utils = baidu.editor.utils
    var uiUtils = baidu.editor.ui.uiUtils
    var domUtils = baidu.editor.dom.domUtils
    var UIBase = baidu.editor.ui.UIBase
    var Popup = (baidu.editor.ui.Popup = function(options) {
      this.initOptions(options)
      this.initPopup()
    })
    var allPopups = []

    function closeAllPopup(evt, el) {
      for (var i = 0; i < allPopups.length; i++) {
        var pop = allPopups[i]
        if (!pop.isHidden()) {
          if (pop.queryAutoHide(el) !== false) {
            if (evt && /scroll/gi.test(evt.type) && pop.className == 'edui-wordpastepop') {
              return
            }
            pop.hide()
          }
        }
      }
      if (allPopups.length) {
        pop.editor.fireEvent('afterhidepop')
      }
    }

    Popup.postHide = closeAllPopup
    var ANCHOR_CLASSES = [
      'edui-anchor-topleft',
      'edui-anchor-topright',
      'edui-anchor-bottomleft',
      'edui-anchor-bottomright'
    ]
    Popup.prototype = {
      SHADOW_RADIUS: 5,
      content: null,
      _hidden: false,
      autoRender: true,
      canSideLeft: true,
      canSideUp: true,
      initPopup: function() {
        this.initUIBase()
        allPopups.push(this)
      },
      getHtmlTpl: function() {
        return (
          '<div id="##" class="edui-popup %%" onmousedown="return false;">' +
          ' <div id="##_body" class="edui-popup-body">' +
          ' <iframe style="position:absolute;z-index:-1;left:0;top:0;background-color: transparent;" frameborder="0" width="100%" height="100%" src="about:blank"></iframe>' +
          ' <div class="edui-shadow"></div>' +
          ' <div id="##_content" class="edui-popup-content">' +
          this.getContentHtmlTpl() +
          '  </div>' +
          ' </div>' +
          '</div>'
        )
      },
      getContentHtmlTpl: function() {
        if (this.content) {
          if (typeof this.content === 'string') {
            return this.content
          }
          return this.content.renderHtml()
        } else {
          return ''
        }
      },
      _UIBase_postRender: UIBase.prototype.postRender,
      postRender: function() {
        if (this.content instanceof UIBase) {
          this.content.postRender()
        }
        if (this.captureWheel && !this.captured) {
          this.captured = true
          var winHeight = (document.documentElement.clientHeight || document.body.clientHeight) - 80
          var _height = this.getDom().offsetHeight
          var _top = uiUtils.getClientRect(this.combox.getDom()).top
          var content = this.getDom('content')
          var ifr = this.getDom('body').getElementsByTagName('iframe')
          var me = this
          ifr.length && (ifr = ifr[0])
          while (_top + _height > winHeight) {
            _height -= 30
          }
          content.style.height = _height + 'px'
          ifr && (ifr.style.height = _height + 'px')
          if (window.XMLHttpRequest) {
            domUtils.on(content, 'onmousewheel' in document.body ? 'mousewheel' : 'DOMMouseScroll', function(e) {
              if (e.preventDefault) {
                e.preventDefault()
              } else {
                e.returnValue = false
              }
              if (e.wheelDelta) {
                content.scrollTop -= (e.wheelDelta / 120) * 60
              } else {
                content.scrollTop -= (e.detail / -3) * 60
              }
            })
          } else {
            domUtils.on(this.getDom(), 'mousewheel', function(e) {
              e.returnValue = false
              me.getDom('content').scrollTop -= (e.wheelDelta / 120) * 60
            })
          }
        }
        this.fireEvent('postRenderAfter')
        this.hide(true)
        this._UIBase_postRender()
      },
      _doAutoRender: function() {
        if (!this.getDom() && this.autoRender) {
          this.render()
        }
      },
      mesureSize: function() {
        var box = this.getDom('content')
        return uiUtils.getClientRect(box)
      },
      fitSize: function() {
        if (this.captureWheel && this.sized) {
          return this.__size
        }
        this.sized = true
        var popBodyEl = this.getDom('body')
        popBodyEl.style.width = ''
        popBodyEl.style.height = ''
        var size = this.mesureSize()
        if (this.captureWheel) {
          popBodyEl.style.width = -(-20 - size.width) + 'px'
          var height = parseInt(this.getDom('content').style.height, 10)
          !window.isNaN(height) && (size.height = height)
        } else {
          popBodyEl.style.width = size.width + 'px'
        }
        popBodyEl.style.height = size.height + 'px'
        this.__size = size
        this.captureWheel && (this.getDom('content').style.overflow = 'auto')
        return size
      },
      showAnchor: function(element, hoz) {
        this.showAnchorRect(uiUtils.getClientRect(element), hoz)
      },
      showAnchorRect: function(rect, hoz, adj) {
        this._doAutoRender()
        var vpRect = uiUtils.getViewportRect()
        this.getDom().style.visibility = 'hidden'
        this._show()
        var popSize = this.fitSize()
        var sideLeft, sideUp, left, top
        if (hoz) {
          sideLeft = this.canSideLeft && rect.right + popSize.width > vpRect.right && rect.left > popSize.width
          sideUp = this.canSideUp && rect.top + popSize.height > vpRect.bottom && rect.bottom > popSize.height
          left = sideLeft ? rect.left - popSize.width : rect.right
          top = sideUp ? rect.bottom - popSize.height : rect.top
        } else {
          sideLeft = this.canSideLeft && rect.right + popSize.width > vpRect.right && rect.left > popSize.width
          sideUp = this.canSideUp && rect.top + popSize.height > vpRect.bottom && rect.bottom > popSize.height
          left = sideLeft ? rect.right - popSize.width : rect.left
          top = sideUp ? rect.top - popSize.height : rect.bottom
        }
        var popEl = this.getDom()
        uiUtils.setViewportOffset(popEl, { left: left, top: top })
        domUtils.removeClasses(popEl, ANCHOR_CLASSES)
        popEl.className += ' ' + ANCHOR_CLASSES[(sideUp ? 1 : 0) * 2 + (sideLeft ? 1 : 0)]
        if (this.editor) {
          popEl.style.zIndex = this.editor.container.style.zIndex * 1 + 10
          baidu.editor.ui.uiUtils.getFixedLayer().style.zIndex = popEl.style.zIndex - 1
        }
        this.getDom().style.visibility = 'visible'
      },
      showAt: function(offset) {
        var left = offset.left
        var top = offset.top
        var rect = { left: left, top: top, right: left, bottom: top, height: 0, width: 0 }
        this.showAnchorRect(rect, false, true)
      },
      _show: function() {
        if (this._hidden) {
          var box = this.getDom()
          box.style.display = ''
          this._hidden = false
          this.fireEvent('show')
        }
      },
      isHidden: function() {
        return this._hidden
      },
      show: function() {
        this._doAutoRender()
        this._show()
      },
      hide: function(notNofity) {
        if (!this._hidden && this.getDom()) {
          this.getDom().style.display = 'none'
          this._hidden = true
          if (!notNofity) {
            this.fireEvent('hide')
          }
        }
      },
      queryAutoHide: function(el) {
        return !el || !uiUtils.contains(this.getDom(), el)
      }
    }
    utils.inherits(Popup, UIBase)
    domUtils.on(document, 'mousedown', function(evt) {
      var el = evt.target || evt.srcElement
      closeAllPopup(evt, el)
    })
    domUtils.on(window, 'scroll', function(evt, el) {
      closeAllPopup(evt, el)
    })
  })()
  ;(function() {
    var utils = baidu.editor.utils
    var UIBase = baidu.editor.ui.UIBase
    var ColorPicker = (baidu.editor.ui.ColorPicker = function(options) {
      this.initOptions(options)
      this.noColorText = this.noColorText || this.editor.getLang('clearColor')
      this.initUIBase()
    })
    ColorPicker.prototype = {
      getHtmlTpl: function() {
        return genColorPicker(this.noColorText, this.editor)
      },
      _onTableClick: function(evt) {
        var tgt = evt.target || evt.srcElement
        var color = tgt.getAttribute('data-color')
        if (color) {
          this.fireEvent('pickcolor', color)
        }
      },
      _onTableOver: function(evt) {
        var tgt = evt.target || evt.srcElement
        var color = tgt.getAttribute('data-color')
        if (color) {
          this.getDom('preview').style.backgroundColor = color
        }
      },
      _onTableOut: function() {
        this.getDom('preview').style.backgroundColor = ''
      },
      _onPickNoColor: function() {
        this.fireEvent('picknocolor')
      }
    }
    utils.inherits(ColorPicker, UIBase)
    var COLORS = (
      'ffffff,000000,eeece1,1f497d,4f81bd,c0504d,9bbb59,8064a2,4bacc6,f79646,' +
      'f2f2f2,7f7f7f,ddd9c3,c6d9f0,dbe5f1,f2dcdb,ebf1dd,e5e0ec,dbeef3,fdeada,' +
      'd8d8d8,595959,c4bd97,8db3e2,b8cce4,e5b9b7,d7e3bc,ccc1d9,b7dde8,fbd5b5,' +
      'bfbfbf,3f3f3f,938953,548dd4,95b3d7,d99694,c3d69b,b2a2c7,92cddc,fac08f,' +
      'a5a5a5,262626,494429,17365d,366092,953734,76923c,5f497a,31859b,e36c09,' +
      '7f7f7f,0c0c0c,1d1b10,0f243e,244061,632423,4f6128,3f3151,205867,974806,' +
      'c00000,ff0000,ffc000,ffff00,92d050,00b050,00b0f0,0070c0,002060,7030a0,'
    ).split(',')

    function genColorPicker(noColorText, editor) {
      var html =
        '<div id="##" class="edui-colorpicker %%">' +
        '<div class="edui-colorpicker-topbar edui-clearfix">' +
        '<div unselectable="on" id="##_preview" class="edui-colorpicker-preview"></div>' +
        '<div unselectable="on" class="edui-colorpicker-nocolor" onclick="$$._onPickNoColor(event, this);">' +
        noColorText +
        '</div>' +
        '</div>' +
        '<table  class="edui-box" style="border-collapse: collapse;" onmouseover="$$._onTableOver(event, this);" onmouseout="$$._onTableOut(event, this);" onclick="return $$._onTableClick(event, this);" cellspacing="0" cellpadding="0">' +
        '<tr style="border-bottom: 1px solid #ddd;font-size: 13px;line-height: 25px;color:#39C;padding-top: 2px"><td colspan="10">' +
        editor.getLang('themeColor') +
        '</td> </tr>' +
        '<tr class="edui-colorpicker-tablefirstrow" >'
      for (var i = 0; i < COLORS.length; i++) {
        if (i && i % 10 === 0) {
          html +=
            '</tr>' +
            (i == 60
              ? '<tr style="border-bottom: 1px solid #ddd;font-size: 13px;line-height: 25px;color:#39C;"><td colspan="10">' +
              editor.getLang('standardColor') +
              '</td></tr>'
              : '') +
            '<tr' +
            (i == 60 ? ' class="edui-colorpicker-tablefirstrow"' : '') +
            '>'
        }
        html +=
          i < 70
            ? '<td style="padding: 0 2px;"><a hidefocus title="' +
            COLORS[i] +
            '" onclick="return false;" href="javascript:" unselectable="on" class="edui-box edui-colorpicker-colorcell"' +
            ' data-color="#' +
            COLORS[i] +
            '"' +
            ' style="background-color:#' +
            COLORS[i] +
            ';border:solid #ccc;' +
            (i < 10 || i >= 60
              ? 'border-width:1px;'
              : i >= 10 && i < 20
                ? 'border-width:1px 1px 0 1px;'
                : 'border-width:0 1px 0 1px;') +
            '"' +
            '></a></td>'
            : ''
      }
      html += '</tr></table></div>'
      return html
    }
  })()
  ;(function() {
    var utils = baidu.editor.utils
    var uiUtils = baidu.editor.ui.uiUtils
    var UIBase = baidu.editor.ui.UIBase
    var TablePicker = (baidu.editor.ui.TablePicker = function(options) {
      this.initOptions(options)
      this.initTablePicker()
    })
    TablePicker.prototype = {
      defaultNumRows: 10,
      defaultNumCols: 10,
      maxNumRows: 20,
      maxNumCols: 20,
      numRows: 10,
      numCols: 10,
      lengthOfCellSide: 22,
      initTablePicker: function() {
        this.initUIBase()
      },
      getHtmlTpl: function() {
        var me = this
        return (
          '<div id="##" class="edui-tablepicker %%">' +
          '<div class="edui-tablepicker-body">' +
          '<div class="edui-infoarea">' +
          '<span id="##_label" class="edui-label"></span>' +
          '</div>' +
          '<div class="edui-pickarea"' +
          ' onmousemove="$$._onMouseMove(event, this);"' +
          ' onmouseover="$$._onMouseOver(event, this);"' +
          ' onmouseout="$$._onMouseOut(event, this);"' +
          ' onclick="$$._onClick(event, this);"' +
          '>' +
          '<div id="##_overlay" class="edui-overlay"></div>' +
          '</div>' +
          '</div>' +
          '</div>'
        )
      },
      _UIBase_render: UIBase.prototype.render,
      render: function(holder) {
        this._UIBase_render(holder)
        this.getDom('label').innerHTML = '0' + this.editor.getLang('t_row') + ' x 0' + this.editor.getLang('t_col')
      },
      _track: function(numCols, numRows) {
        var style = this.getDom('overlay').style
        var sideLen = this.lengthOfCellSide
        style.width = numCols * sideLen + 'px'
        style.height = numRows * sideLen + 'px'
        var label = this.getDom('label')
        label.innerHTML = numCols + this.editor.getLang('t_col') + ' x ' + numRows + this.editor.getLang('t_row')
        this.numCols = numCols
        this.numRows = numRows
      },
      _onMouseOver: function(evt, el) {
        var rel = evt.relatedTarget || evt.fromElement
        if (!uiUtils.contains(el, rel) && el !== rel) {
          this.getDom('label').innerHTML = '0' + this.editor.getLang('t_col') + ' x 0' + this.editor.getLang('t_row')
          this.getDom('overlay').style.visibility = ''
        }
      },
      _onMouseOut: function(evt, el) {
        var rel = evt.relatedTarget || evt.toElement
        if (!uiUtils.contains(el, rel) && el !== rel) {
          this.getDom('label').innerHTML = '0' + this.editor.getLang('t_col') + ' x 0' + this.editor.getLang('t_row')
          this.getDom('overlay').style.visibility = 'hidden'
        }
      },
      _onMouseMove: function(evt, el) {
        var style = this.getDom('overlay').style
        var offset = uiUtils.getEventOffset(evt)
        var sideLen = this.lengthOfCellSide
        var numCols = Math.ceil(offset.left / sideLen)
        var numRows = Math.ceil(offset.top / sideLen)
        this._track(numCols, numRows)
      },
      _onClick: function() {
        this.fireEvent('picktable', this.numCols, this.numRows)
      }
    }
    utils.inherits(TablePicker, UIBase)
  })()
  ;(function() {
    var browser = baidu.editor.browser
    var domUtils = baidu.editor.dom.domUtils
    var uiUtils = baidu.editor.ui.uiUtils
    var TPL_STATEFUL =
      'onmousedown="$$.Stateful_onMouseDown(event, this);"' +
      ' onmouseup="$$.Stateful_onMouseUp(event, this);"' +
      (browser.ie
        ? ' onmouseenter="$$.Stateful_onMouseEnter(event, this);"' +
        ' onmouseleave="$$.Stateful_onMouseLeave(event, this);"'
        : ' onmouseover="$$.Stateful_onMouseOver(event, this);"' + ' onmouseout="$$.Stateful_onMouseOut(event, this);"')
    baidu.editor.ui.Stateful = {
      alwalysHoverable: false,
      target: null,
      Stateful_init: function() {
        this._Stateful_dGetHtmlTpl = this.getHtmlTpl
        this.getHtmlTpl = this.Stateful_getHtmlTpl
      },
      Stateful_getHtmlTpl: function() {
        var tpl = this._Stateful_dGetHtmlTpl()
        return tpl.replace(/stateful/g, function() {
          return TPL_STATEFUL
        })
      },
      Stateful_onMouseEnter: function(evt, el) {
        this.target = el
        if (!this.isDisabled() || this.alwalysHoverable) {
          this.addState('hover')
          this.fireEvent('over')
        }
      },
      Stateful_onMouseLeave: function(evt, el) {
        if (!this.isDisabled() || this.alwalysHoverable) {
          this.removeState('hover')
          this.removeState('active')
          this.fireEvent('out')
        }
      },
      Stateful_onMouseOver: function(evt, el) {
        var rel = evt.relatedTarget
        if (!uiUtils.contains(el, rel) && el !== rel) {
          this.Stateful_onMouseEnter(evt, el)
        }
      },
      Stateful_onMouseOut: function(evt, el) {
        var rel = evt.relatedTarget
        if (!uiUtils.contains(el, rel) && el !== rel) {
          this.Stateful_onMouseLeave(evt, el)
        }
      },
      Stateful_onMouseDown: function(evt, el) {
        if (!this.isDisabled()) {
          this.addState('active')
        }
      },
      Stateful_onMouseUp: function(evt, el) {
        if (!this.isDisabled()) {
          this.removeState('active')
        }
      },
      Stateful_postRender: function() {
        if (this.disabled && !this.hasState('disabled')) {
          this.addState('disabled')
        }
      },
      hasState: function(state) {
        return domUtils.hasClass(this.getStateDom(), 'edui-state-' + state)
      },
      addState: function(state) {
        if (!this.hasState(state)) {
          this.getStateDom().className += ' edui-state-' + state
        }
      },
      removeState: function(state) {
        if (this.hasState(state)) {
          domUtils.removeClasses(this.getStateDom(), ['edui-state-' + state])
        }
      },
      getStateDom: function() {
        return this.getDom('state')
      },
      isChecked: function() {
        return this.hasState('checked')
      },
      setChecked: function(checked) {
        if (!this.isDisabled() && checked) {
          this.addState('checked')
        } else {
          this.removeState('checked')
        }
      },
      isDisabled: function() {
        return this.hasState('disabled')
      },
      setDisabled: function(disabled) {
        if (disabled) {
          this.removeState('hover')
          this.removeState('checked')
          this.removeState('active')
          this.addState('disabled')
        } else {
          this.removeState('disabled')
        }
      }
    }
  })()
  ;(function() {
    var utils = baidu.editor.utils
    var UIBase = baidu.editor.ui.UIBase
    var Stateful = baidu.editor.ui.Stateful
    var Button = (baidu.editor.ui.Button = function(options) {
      if (options.name) {
        var btnName = options.name
        var cssRules = options.cssRules
        if (!options.className) {
          options.className = 'edui-for-' + btnName
        }
        options.cssRules = '.edui-default  .edui-for-' + btnName + ' .edui-icon {' + cssRules + '}'
      }
      this.initOptions(options)
      this.initButton()
    })
    Button.prototype = {
      uiName: 'button',
      label: '',
      title: '',
      showIcon: true,
      showText: true,
      cssRules: '',
      initButton: function() {
        this.initUIBase()
        this.Stateful_init()
        if (this.cssRules) {
          utils.cssRule('edui-customize-' + this.name + '-style', this.cssRules)
        }
      },
      getHtmlTpl: function() {
        return (
          '<div id="##" class="edui-box %%">' +
          '<div id="##_state" stateful>' +
          '<div class="%%-wrap"><div id="##_body" unselectable="on" ' +
          (this.title ? 'title="' + this.title + '"' : '') +
          ' class="%%-body" onmousedown="return $$._onMouseDown(event, this);" onclick="return $$._onClick(event, this);">' +
          (this.showIcon ? '<div class="edui-box edui-icon"></div>' : '') +
          (this.showText ? '<div class="edui-box edui-label">' + this.label + '</div>' : '') +
          '</div>' +
          '</div>' +
          '</div></div>'
        )
      },
      postRender: function() {
        this.Stateful_postRender()
        this.setDisabled(this.disabled)
      },
      _onMouseDown: function(e) {
        var target = e.target || e.srcElement
        var tagName = target && target.tagName && target.tagName.toLowerCase()
        if (tagName == 'input' || tagName == 'object' || tagName == 'object') {
          return false
        }
      },
      _onClick: function() {
        if (!this.isDisabled()) {
          this.fireEvent('click')
        }
      },
      setTitle: function(text) {
        var label = this.getDom('label')
        label.innerHTML = text
      }
    }
    utils.inherits(Button, UIBase)
    utils.extend(Button.prototype, Stateful)
  })()
  ;(function() {
    var utils = baidu.editor.utils
    var uiUtils = baidu.editor.ui.uiUtils
    var domUtils = baidu.editor.dom.domUtils
    var UIBase = baidu.editor.ui.UIBase
    var Stateful = baidu.editor.ui.Stateful
    var SplitButton = (baidu.editor.ui.SplitButton = function(options) {
      this.initOptions(options)
      this.initSplitButton()
    })
    SplitButton.prototype = {
      popup: null,
      uiName: 'splitbutton',
      title: '',
      initSplitButton: function() {
        this.initUIBase()
        this.Stateful_init()
        var me = this
        if (this.popup != null) {
          var popup = this.popup
          this.popup = null
          this.setPopup(popup)
        }
      },
      _UIBase_postRender: UIBase.prototype.postRender,
      postRender: function() {
        this.Stateful_postRender()
        this._UIBase_postRender()
      },
      setPopup: function(popup) {
        if (this.popup === popup) {
          return
        }
        if (this.popup != null) {
          this.popup.dispose()
        }
        popup.addListener('show', utils.bind(this._onPopupShow, this))
        popup.addListener('hide', utils.bind(this._onPopupHide, this))
        popup.addListener(
          'postrender',
          utils.bind(function() {
            popup
              .getDom('body')
              .appendChild(
                uiUtils.createElementByHtml(
                  '<div id="' +
                  this.popup.id +
                  '_bordereraser" class="edui-bordereraser edui-background" style="width:' +
                  (uiUtils.getClientRect(this.getDom()).width + 20) +
                  'px"></div>'
                )
              )
            popup.getDom().className += ' ' + this.className
          }, this)
        )
        this.popup = popup
      },
      _onPopupShow: function() {
        this.addState('opened')
      },
      _onPopupHide: function() {
        this.removeState('opened')
      },
      getHtmlTpl: function() {
        return (
          '<div id="##" class="edui-box %%">' +
          '<div ' +
          (this.title ? 'title="' + this.title + '"' : '') +
          ' id="##_state" stateful><div class="%%-body">' +
          '<div id="##_button_body" class="edui-box edui-button-body" onclick="$$._onButtonClick(event, this);">' +
          '<div class="edui-box edui-icon"></div>' +
          '</div>' +
          '<div class="edui-box edui-splitborder"></div>' +
          '<div class="edui-box edui-arrow" onclick="$$._onArrowClick();"></div>' +
          '</div></div></div>'
        )
      },
      showPopup: function() {
        var rect = uiUtils.getClientRect(this.getDom())
        rect.top -= this.popup.SHADOW_RADIUS
        rect.height += this.popup.SHADOW_RADIUS
        this.popup.showAnchorRect(rect)
      },
      _onArrowClick: function(event, el) {
        if (!this.isDisabled()) {
          this.showPopup()
        }
      },
      _onButtonClick: function() {
        if (!this.isDisabled()) {
          this.fireEvent('buttonclick')
        }
      }
    }
    utils.inherits(SplitButton, UIBase)
    utils.extend(SplitButton.prototype, Stateful, true)
  })()
  ;(function() {
    var utils = baidu.editor.utils
    var uiUtils = baidu.editor.ui.uiUtils
    var ColorPicker = baidu.editor.ui.ColorPicker
    var Popup = baidu.editor.ui.Popup
    var SplitButton = baidu.editor.ui.SplitButton
    var ColorButton = (baidu.editor.ui.ColorButton = function(options) {
      this.initOptions(options)
      this.initColorButton()
    })
    ColorButton.prototype = {
      initColorButton: function() {
        var me = this
        this.popup = new Popup({
          content: new ColorPicker({
            noColorText: me.editor.getLang('clearColor'),
            editor: me.editor,
            onpickcolor: function(t, color) {
              me._onPickColor(color)
            },
            onpicknocolor: function(t, color) {
              me._onPickNoColor(color)
            }
          }),
          editor: me.editor
        })
        this.initSplitButton()
      },
      _SplitButton_postRender: SplitButton.prototype.postRender,
      postRender: function() {
        this._SplitButton_postRender()
        this.getDom('button_body').appendChild(
          uiUtils.createElementByHtml('<div id="' + this.id + '_colorlump" class="edui-colorlump"></div>')
        )
        this.getDom().className += ' edui-colorbutton'
      },
      setColor: function(color) {
        this.getDom('colorlump').style.backgroundColor = color
        this.color = color
      },
      _onPickColor: function(color) {
        if (this.fireEvent('pickcolor', color) !== false) {
          this.setColor(color)
          this.popup.hide()
        }
      },
      _onPickNoColor: function(color) {
        if (this.fireEvent('picknocolor') !== false) {
          this.popup.hide()
        }
      }
    }
    utils.inherits(ColorButton, SplitButton)
  })()
  ;(function() {
    var utils = baidu.editor.utils
    var Popup = baidu.editor.ui.Popup
    var TablePicker = baidu.editor.ui.TablePicker
    var SplitButton = baidu.editor.ui.SplitButton
    var TableButton = (baidu.editor.ui.TableButton = function(options) {
      this.initOptions(options)
      this.initTableButton()
    })
    TableButton.prototype = {
      initTableButton: function() {
        var me = this
        this.popup = new Popup({
          content: new TablePicker({
            editor: me.editor,
            onpicktable: function(t, numCols, numRows) {
              me._onPickTable(numCols, numRows)
            }
          }),
          editor: me.editor
        })
        this.initSplitButton()
      },
      _onPickTable: function(numCols, numRows) {
        if (this.fireEvent('picktable', numCols, numRows) !== false) {
          this.popup.hide()
        }
      }
    }
    utils.inherits(TableButton, SplitButton)
  })()
  ;(function() {
    var utils = baidu.editor.utils
    var UIBase = baidu.editor.ui.UIBase
    var AutoTypeSetPicker = (baidu.editor.ui.AutoTypeSetPicker = function(options) {
      this.initOptions(options)
      this.initAutoTypeSetPicker()
    })
    AutoTypeSetPicker.prototype = {
      initAutoTypeSetPicker: function() {
        this.initUIBase()
      },
      getHtmlTpl: function() {
        var me = this.editor
        var opt = me.options.autotypeset
        var lang = me.getLang('autoTypeSet')
        var textAlignInputName = 'textAlignValue' + me.uid
        var imageBlockInputName = 'imageBlockLineValue' + me.uid
        var symbolConverInputName = 'symbolConverValue' + me.uid
        return (
          '<div id="##" class="edui-autotypesetpicker %%">' +
          '<div class="edui-autotypesetpicker-body">' +
          '<table >' +
          '<tr><td nowrap><input type="checkbox" name="mergeEmptyline" ' +
          (opt['mergeEmptyline'] ? 'checked' : '') +
          '>' +
          lang.mergeLine +
          '</td><td colspan="2"><input type="checkbox" name="removeEmptyline" ' +
          (opt['removeEmptyline'] ? 'checked' : '') +
          '>' +
          lang.delLine +
          '</td></tr>' +
          '<tr><td nowrap><input type="checkbox" name="removeClass" ' +
          (opt['removeClass'] ? 'checked' : '') +
          '>' +
          lang.removeFormat +
          '</td><td colspan="2"><input type="checkbox" name="indent" ' +
          (opt['indent'] ? 'checked' : '') +
          '>' +
          lang.indent +
          '</td></tr>' +
          '<tr>' +
          '<td nowrap><input type="checkbox" name="textAlign" ' +
          (opt['textAlign'] ? 'checked' : '') +
          '>' +
          lang.alignment +
          '</td>' +
          '<td colspan="2" id="' +
          textAlignInputName +
          '">' +
          '<input type="radio" name="' +
          textAlignInputName +
          '" value="left" ' +
          (opt['textAlign'] && opt['textAlign'] == 'left' ? 'checked' : '') +
          '>' +
          me.getLang('justifyleft') +
          '<input type="radio" name="' +
          textAlignInputName +
          '" value="center" ' +
          (opt['textAlign'] && opt['textAlign'] == 'center' ? 'checked' : '') +
          '>' +
          me.getLang('justifycenter') +
          '<input type="radio" name="' +
          textAlignInputName +
          '" value="right" ' +
          (opt['textAlign'] && opt['textAlign'] == 'right' ? 'checked' : '') +
          '>' +
          me.getLang('justifyright') +
          '</td>' +
          '</tr>' +
          '<tr>' +
          '<td nowrap><input type="checkbox" name="imageBlockLine" ' +
          (opt['imageBlockLine'] ? 'checked' : '') +
          '>' +
          lang.imageFloat +
          '</td>' +
          '<td nowrap id="' +
          imageBlockInputName +
          '">' +
          '<input type="radio" name="' +
          imageBlockInputName +
          '" value="none" ' +
          (opt['imageBlockLine'] && opt['imageBlockLine'] == 'none' ? 'checked' : '') +
          '>' +
          me.getLang('default') +
          '<input type="radio" name="' +
          imageBlockInputName +
          '" value="left" ' +
          (opt['imageBlockLine'] && opt['imageBlockLine'] == 'left' ? 'checked' : '') +
          '>' +
          me.getLang('justifyleft') +
          '<input type="radio" name="' +
          imageBlockInputName +
          '" value="center" ' +
          (opt['imageBlockLine'] && opt['imageBlockLine'] == 'center' ? 'checked' : '') +
          '>' +
          me.getLang('justifycenter') +
          '<input type="radio" name="' +
          imageBlockInputName +
          '" value="right" ' +
          (opt['imageBlockLine'] && opt['imageBlockLine'] == 'right' ? 'checked' : '') +
          '>' +
          me.getLang('justifyright') +
          '</td>' +
          '</tr>' +
          '<tr><td nowrap><input type="checkbox" name="clearFontSize" ' +
          (opt['clearFontSize'] ? 'checked' : '') +
          '>' +
          lang.removeFontsize +
          '</td><td colspan="2"><input type="checkbox" name="clearFontFamily" ' +
          (opt['clearFontFamily'] ? 'checked' : '') +
          '>' +
          lang.removeFontFamily +
          '</td></tr>' +
          '<tr><td nowrap colspan="3"><input type="checkbox" name="removeEmptyNode" ' +
          (opt['removeEmptyNode'] ? 'checked' : '') +
          '>' +
          lang.removeHtml +
          '</td></tr>' +
          '<tr><td nowrap colspan="3"><input type="checkbox" name="pasteFilter" ' +
          (opt['pasteFilter'] ? 'checked' : '') +
          '>' +
          lang.pasteFilter +
          '</td></tr>' +
          '<tr>' +
          '<td nowrap><input type="checkbox" name="symbolConver" ' +
          (opt['bdc2sb'] || opt['tobdc'] ? 'checked' : '') +
          '>' +
          lang.symbol +
          '</td>' +
          '<td id="' +
          symbolConverInputName +
          '">' +
          '<input type="radio" name="bdc" value="bdc2sb" ' +
          (opt['bdc2sb'] ? 'checked' : '') +
          '>' +
          lang.bdc2sb +
          '<input type="radio" name="bdc" value="tobdc" ' +
          (opt['tobdc'] ? 'checked' : '') +
          '>' +
          lang.tobdc +
          '' +
          '</td>' +
          '<td nowrap align="right"><button >' +
          lang.run +
          '</button></td>' +
          '</tr>' +
          '</table>' +
          '</div>' +
          '</div>'
        )
      },
      _UIBase_render: UIBase.prototype.render
    }
    utils.inherits(AutoTypeSetPicker, UIBase)
  })()
  ;(function() {
    var utils = baidu.editor.utils
    var Popup = baidu.editor.ui.Popup
    var AutoTypeSetPicker = baidu.editor.ui.AutoTypeSetPicker
    var SplitButton = baidu.editor.ui.SplitButton
    var AutoTypeSetButton = (baidu.editor.ui.AutoTypeSetButton = function(options) {
      this.initOptions(options)
      this.initAutoTypeSetButton()
    })

    function getPara(me) {
      var opt = {}
      var cont = me.getDom()
      var editorId = me.editor.uid
      var inputType = null
      var attrName = null
      var ipts = domUtils.getElementsByTagName(cont, 'input')
      for (var i = ipts.length - 1, ipt; (ipt = ipts[i--]);) {
        inputType = ipt.getAttribute('type')
        if (inputType == 'checkbox') {
          attrName = ipt.getAttribute('name')
          opt[attrName] && delete opt[attrName]
          if (ipt.checked) {
            var attrValue = document.getElementById(attrName + 'Value' + editorId)
            if (attrValue) {
              if (/input/gi.test(attrValue.tagName)) {
                opt[attrName] = attrValue.value
              } else {
                var iptChilds = attrValue.getElementsByTagName('input')
                for (var j = iptChilds.length - 1, iptchild; (iptchild = iptChilds[j--]);) {
                  if (iptchild.checked) {
                    opt[attrName] = iptchild.value
                    break
                  }
                }
              }
            } else {
              opt[attrName] = true
            }
          } else {
            opt[attrName] = false
          }
        } else {
          opt[ipt.getAttribute('value')] = ipt.checked
        }
      }
      var selects = domUtils.getElementsByTagName(cont, 'select')
      for (var i = 0, si; (si = selects[i++]);) {
        var attr = si.getAttribute('name')
        opt[attr] = opt[attr] ? si.value : ''
      }
      utils.extend(me.editor.options.autotypeset, opt)
      me.editor.setPreferences('autotypeset', opt)
    }

    AutoTypeSetButton.prototype = {
      initAutoTypeSetButton: function() {
        var me = this
        this.popup = new Popup({
          content: new AutoTypeSetPicker({ editor: me.editor }),
          editor: me.editor,
          hide: function() {
            if (!this._hidden && this.getDom()) {
              getPara(this)
              this.getDom().style.display = 'none'
              this._hidden = true
              this.fireEvent('hide')
            }
          }
        })
        var flag = 0
        this.popup.addListener('postRenderAfter', function() {
          var popupUI = this
          if (flag) {
            return
          }
          var cont = this.getDom()
          var btn = cont.getElementsByTagName('button')[0]
          btn.onclick = function() {
            getPara(popupUI)
            me.editor.execCommand('autotypeset')
            popupUI.hide()
          }
          domUtils.on(cont, 'click', function(e) {
            var target = e.target || e.srcElement
            var editorId = me.editor.uid
            if (target && target.tagName == 'INPUT') {
              if (target.name == 'imageBlockLine' || target.name == 'textAlign' || target.name == 'symbolConver') {
                var checked = target.checked
                var radioTd = document.getElementById(target.name + 'Value' + editorId)
                var radios = radioTd.getElementsByTagName('input')
                var defalutSelect = { imageBlockLine: 'none', textAlign: 'left', symbolConver: 'tobdc' }
                for (var i = 0; i < radios.length; i++) {
                  if (checked) {
                    if (radios[i].value == defalutSelect[target.name]) {
                      radios[i].checked = 'checked'
                    }
                  } else {
                    radios[i].checked = false
                  }
                }
              }
              if (
                target.name == 'imageBlockLineValue' + editorId ||
                target.name == 'textAlignValue' + editorId ||
                target.name == 'bdc'
              ) {
                var checkboxs = target.parentNode.previousSibling.getElementsByTagName('input')
                checkboxs && (checkboxs[0].checked = true)
              }
              getPara(popupUI)
            }
          })
          flag = 1
        })
        this.initSplitButton()
      }
    }
    utils.inherits(AutoTypeSetButton, SplitButton)
  })()
  ;(function() {
    var utils = baidu.editor.utils
    var Popup = baidu.editor.ui.Popup
    var Stateful = baidu.editor.ui.Stateful
    var UIBase = baidu.editor.ui.UIBase
    var CellAlignPicker = (baidu.editor.ui.CellAlignPicker = function(options) {
      this.initOptions(options)
      this.initSelected()
      this.initCellAlignPicker()
    })
    CellAlignPicker.prototype = {
      initSelected: function() {
        var status = { valign: { top: 0, middle: 1, bottom: 2 }, align: { left: 0, center: 1, right: 2 }, count: 3 }
        var result = -1
        if (this.selected) {
          this.selectedIndex = status.valign[this.selected.valign] * status.count + status.align[this.selected.align]
        }
      },
      initCellAlignPicker: function() {
        this.initUIBase()
        this.Stateful_init()
      },
      getHtmlTpl: function() {
        var alignType = ['left', 'center', 'right']
        var COUNT = 9
        var tempClassName = null
        var tempIndex = -1
        var tmpl = []
        for (var i = 0; i < COUNT; i++) {
          tempClassName = this.selectedIndex === i ? ' class="edui-cellalign-selected" ' : ''
          tempIndex = i % 3
          tempIndex === 0 && tmpl.push('<tr>')
          tmpl.push(
            '<td index="' +
            i +
            '" ' +
            tempClassName +
            ' stateful><div class="edui-icon edui-' +
            alignType[tempIndex] +
            '"></div></td>'
          )
          tempIndex === 2 && tmpl.push('</tr>')
        }
        return (
          '<div id="##" class="edui-cellalignpicker %%">' +
          '<div class="edui-cellalignpicker-body">' +
          '<table onclick="$$._onClick(event);">' +
          tmpl.join('') +
          '</table>' +
          '</div>' +
          '</div>'
        )
      },
      getStateDom: function() {
        return this.target
      },
      _onClick: function(evt) {
        var target = evt.target || evt.srcElement
        if (/icon/.test(target.className)) {
          this.items[target.parentNode.getAttribute('index')].onclick()
          Popup.postHide(evt)
        }
      },
      _UIBase_render: UIBase.prototype.render
    }
    utils.inherits(CellAlignPicker, UIBase)
    utils.extend(CellAlignPicker.prototype, Stateful, true)
  })()
  ;(function() {
    var utils = baidu.editor.utils
    var Stateful = baidu.editor.ui.Stateful
    var uiUtils = baidu.editor.ui.uiUtils
    var UIBase = baidu.editor.ui.UIBase
    var PastePicker = (baidu.editor.ui.PastePicker = function(options) {
      this.initOptions(options)
      this.initPastePicker()
    })
    PastePicker.prototype = {
      initPastePicker: function() {
        this.initUIBase()
        this.Stateful_init()
      },
      getHtmlTpl: function() {
        return (
          '<div class="edui-pasteicon" onclick="$$._onClick(this)"></div>' +
          '<div class="edui-pastecontainer">' +
          '<div class="edui-title">' +
          this.editor.getLang('pasteOpt') +
          '</div>' +
          '<div class="edui-button">' +
          '<div title="' +
          this.editor.getLang('pasteSourceFormat') +
          '" onclick="$$.format(false)" stateful>' +
          '<div class="edui-richtxticon"></div></div>' +
          '<div title="' +
          this.editor.getLang('tagFormat') +
          '" onclick="$$.format(2)" stateful>' +
          '<div class="edui-tagicon"></div></div>' +
          '<div title="' +
          this.editor.getLang('pasteTextFormat') +
          '" onclick="$$.format(true)" stateful>' +
          '<div class="edui-plaintxticon"></div></div>' +
          '</div>' +
          '</div>' +
          '</div>'
        )
      },
      getStateDom: function() {
        return this.target
      },
      format: function(param) {
        this.editor.ui._isTransfer = true
        this.editor.fireEvent('pasteTransfer', param)
      },
      _onClick: function(cur) {
        var node = domUtils.getNextDomNode(cur)
        var screenHt = uiUtils.getViewportRect().height
        var subPop = uiUtils.getClientRect(node)
        if (subPop.top + subPop.height > screenHt) {
          node.style.top = -subPop.height - cur.offsetHeight + 'px'
        } else {
          node.style.top = ''
        }
        if (/hidden/gi.test(domUtils.getComputedStyle(node, 'visibility'))) {
          node.style.visibility = 'visible'
          domUtils.addClass(cur, 'edui-state-opened')
        } else {
          node.style.visibility = 'hidden'
          domUtils.removeClasses(cur, 'edui-state-opened')
        }
      },
      _UIBase_render: UIBase.prototype.render
    }
    utils.inherits(PastePicker, UIBase)
    utils.extend(PastePicker.prototype, Stateful, true)
  })()
  ;(function() {
    var utils = baidu.editor.utils
    var uiUtils = baidu.editor.ui.uiUtils
    var UIBase = baidu.editor.ui.UIBase
    var Toolbar = (baidu.editor.ui.Toolbar = function(options) {
      this.initOptions(options)
      this.initToolbar()
    })
    Toolbar.prototype = {
      items: null,
      initToolbar: function() {
        this.items = this.items || []
        this.initUIBase()
      },
      add: function(item, index) {
        if (index === undefined) {
          this.items.push(item)
        } else {
          this.items.splice(index, 0, item)
        }
      },
      getHtmlTpl: function() {
        var buff = []
        for (var i = 0; i < this.items.length; i++) {
          buff[i] = this.items[i].renderHtml()
        }
        return (
          '<div id="##" class="edui-toolbar %%" onselectstart="return false;" onmousedown="return $$._onMouseDown(event, this);">' +
          buff.join('') +
          '</div>'
        )
      },
      postRender: function() {
        var box = this.getDom()
        for (var i = 0; i < this.items.length; i++) {
          this.items[i].postRender()
        }
        uiUtils.makeUnselectable(box)
      },
      _onMouseDown: function(e) {
        var target = e.target || e.srcElement
        var tagName = target && target.tagName && target.tagName.toLowerCase()
        if (tagName == 'input' || tagName == 'object' || tagName == 'object') {
          return false
        }
      }
    }
    utils.inherits(Toolbar, UIBase)
  })()
  ;(function() {
    var utils = baidu.editor.utils
    var domUtils = baidu.editor.dom.domUtils
    var uiUtils = baidu.editor.ui.uiUtils
    var UIBase = baidu.editor.ui.UIBase
    var Popup = baidu.editor.ui.Popup
    var Stateful = baidu.editor.ui.Stateful
    var CellAlignPicker = baidu.editor.ui.CellAlignPicker
    var Menu = (baidu.editor.ui.Menu = function(options) {
      this.initOptions(options)
      this.initMenu()
    })
    var menuSeparator = {
      renderHtml: function() {
        return '<div class="edui-menuitem edui-menuseparator"><div class="edui-menuseparator-inner"></div></div>'
      },
      postRender: function() {
      },
      queryAutoHide: function() {
        return true
      }
    }
    Menu.prototype = {
      items: null,
      uiName: 'menu',
      initMenu: function() {
        this.items = this.items || []
        this.initPopup()
        this.initItems()
      },
      initItems: function() {
        for (var i = 0; i < this.items.length; i++) {
          var item = this.items[i]
          if (item == '-') {
            this.items[i] = this.getSeparator()
          } else {
            if (!(item instanceof MenuItem)) {
              item.editor = this.editor
              item.theme = this.editor.options.theme
              this.items[i] = this.createItem(item)
            }
          }
        }
      },
      getSeparator: function() {
        return menuSeparator
      },
      createItem: function(item) {
        item.menu = this
        return new MenuItem(item)
      },
      _Popup_getContentHtmlTpl: Popup.prototype.getContentHtmlTpl,
      getContentHtmlTpl: function() {
        if (this.items.length == 0) {
          return this._Popup_getContentHtmlTpl()
        }
        var buff = []
        for (var i = 0; i < this.items.length; i++) {
          var item = this.items[i]
          buff[i] = item.renderHtml()
        }
        return '<div class="%%-body">' + buff.join('') + '</div>'
      },
      _Popup_postRender: Popup.prototype.postRender,
      postRender: function() {
        var me = this
        for (var i = 0; i < this.items.length; i++) {
          var item = this.items[i]
          item.ownerMenu = this
          item.postRender()
        }
        domUtils.on(this.getDom(), 'mouseover', function(evt) {
          evt = evt || event
          var rel = evt.relatedTarget || evt.fromElement
          var el = me.getDom()
          if (!uiUtils.contains(el, rel) && el !== rel) {
            me.fireEvent('over')
          }
        })
        this._Popup_postRender()
      },
      queryAutoHide: function(el) {
        if (el) {
          if (uiUtils.contains(this.getDom(), el)) {
            return false
          }
          for (var i = 0; i < this.items.length; i++) {
            var item = this.items[i]
            if (item.queryAutoHide(el) === false) {
              return false
            }
          }
        }
      },
      clearItems: function() {
        for (var i = 0; i < this.items.length; i++) {
          var item = this.items[i]
          clearTimeout(item._showingTimer)
          clearTimeout(item._closingTimer)
          if (item.subMenu) {
            item.subMenu.destroy()
          }
        }
        this.items = []
      },
      destroy: function() {
        if (this.getDom()) {
          domUtils.remove(this.getDom())
        }
        this.clearItems()
      },
      dispose: function() {
        this.destroy()
      }
    }
    utils.inherits(Menu, Popup)
    var MenuItem = (baidu.editor.ui.MenuItem = function(options) {
      this.initOptions(options)
      this.initUIBase()
      this.Stateful_init()
      if (this.subMenu && !(this.subMenu instanceof Menu)) {
        if (options.className && options.className.indexOf('aligntd') != -1) {
          var me = this
          this.subMenu.selected = this.editor.queryCommandValue('cellalignment')
          this.subMenu = new Popup({
            content: new CellAlignPicker(this.subMenu),
            parentMenu: me,
            editor: me.editor,
            destroy: function() {
              if (this.getDom()) {
                domUtils.remove(this.getDom())
              }
            }
          })
          this.subMenu.addListener('postRenderAfter', function() {
            domUtils.on(this.getDom(), 'mouseover', function() {
              me.addState('opened')
            })
          })
        } else {
          this.subMenu = new Menu(this.subMenu)
        }
      }
    })
    MenuItem.prototype = {
      label: '',
      subMenu: null,
      ownerMenu: null,
      uiName: 'menuitem',
      alwalysHoverable: true,
      getHtmlTpl: function() {
        return (
          '<div id="##" class="%%" stateful onclick="$$._onClick(event, this);">' +
          '<div class="%%-body">' +
          this.renderLabelHtml() +
          '</div>' +
          '</div>'
        )
      },
      postRender: function() {
        var me = this
        this.addListener('over', function() {
          me.ownerMenu.fireEvent('submenuover', me)
          if (me.subMenu) {
            me.delayShowSubMenu()
          }
        })
        if (this.subMenu) {
          this.getDom().className += ' edui-hassubmenu'
          this.subMenu.render()
          this.addListener('out', function() {
            me.delayHideSubMenu()
          })
          this.subMenu.addListener('over', function() {
            clearTimeout(me._closingTimer)
            me._closingTimer = null
            me.addState('opened')
          })
          this.ownerMenu.addListener('hide', function() {
            me.hideSubMenu()
          })
          this.ownerMenu.addListener('submenuover', function(t, subMenu) {
            if (subMenu !== me) {
              me.delayHideSubMenu()
            }
          })
          this.subMenu._bakQueryAutoHide = this.subMenu.queryAutoHide
          this.subMenu.queryAutoHide = function(el) {
            if (el && uiUtils.contains(me.getDom(), el)) {
              return false
            }
            return this._bakQueryAutoHide(el)
          }
        }
        this.getDom().style.tabIndex = '-1'
        uiUtils.makeUnselectable(this.getDom())
        this.Stateful_postRender()
      },
      delayShowSubMenu: function() {
        var me = this
        if (!me.isDisabled()) {
          me.addState('opened')
          clearTimeout(me._showingTimer)
          clearTimeout(me._closingTimer)
          me._closingTimer = null
          me._showingTimer = setTimeout(function() {
            me.showSubMenu()
          }, 250)
        }
      },
      delayHideSubMenu: function() {
        var me = this
        if (!me.isDisabled()) {
          me.removeState('opened')
          clearTimeout(me._showingTimer)
          if (!me._closingTimer) {
            me._closingTimer = setTimeout(function() {
              if (!me.hasState('opened')) {
                me.hideSubMenu()
              }
              me._closingTimer = null
            }, 400)
          }
        }
      },
      renderLabelHtml: function() {
        return (
          '<div class="edui-arrow"></div>' +
          '<div class="edui-box edui-icon"></div>' +
          '<div class="edui-box edui-label %%-label">' +
          (this.label || '') +
          '</div>'
        )
      },
      getStateDom: function() {
        return this.getDom()
      },
      queryAutoHide: function(el) {
        if (this.subMenu && this.hasState('opened')) {
          return this.subMenu.queryAutoHide(el)
        }
      },
      _onClick: function(event, this_) {
        if (this.hasState('disabled')) {
          return
        }
        if (this.fireEvent('click', event, this_) !== false) {
          if (this.subMenu) {
            this.showSubMenu()
          } else {
            Popup.postHide(event)
          }
        }
      },
      showSubMenu: function() {
        var rect = uiUtils.getClientRect(this.getDom())
        rect.right -= 5
        rect.left += 2
        rect.width -= 7
        rect.top -= 4
        rect.bottom += 4
        rect.height += 8
        this.subMenu.showAnchorRect(rect, true, true)
      },
      hideSubMenu: function() {
        this.subMenu.hide()
      }
    }
    utils.inherits(MenuItem, UIBase)
    utils.extend(MenuItem.prototype, Stateful, true)
  })()
  ;(function() {
    var utils = baidu.editor.utils
    var uiUtils = baidu.editor.ui.uiUtils
    var Menu = baidu.editor.ui.Menu
    var SplitButton = baidu.editor.ui.SplitButton
    var Combox = (baidu.editor.ui.Combox = function(options) {
      this.initOptions(options)
      this.initCombox()
    })
    Combox.prototype = {
      uiName: 'combox',
      onbuttonclick: function() {
        this.showPopup()
      },
      initCombox: function() {
        var me = this
        this.items = this.items || []
        for (var i = 0; i < this.items.length; i++) {
          var item = this.items[i]
          item.uiName = 'listitem'
          item.index = i
          item.onclick = function() {
            me.selectByIndex(this.index)
          }
        }
        this.popup = new Menu({
          items: this.items,
          uiName: 'list',
          editor: this.editor,
          captureWheel: true,
          combox: this
        })
        this.initSplitButton()
      },
      _SplitButton_postRender: SplitButton.prototype.postRender,
      postRender: function() {
        this._SplitButton_postRender()
        this.setLabel(this.label || '')
        this.setValue(this.initValue || '')
      },
      showPopup: function() {
        var rect = uiUtils.getClientRect(this.getDom())
        rect.top += 1
        rect.bottom -= 1
        rect.height -= 2
        this.popup.showAnchorRect(rect)
      },
      getValue: function() {
        return this.value
      },
      setValue: function(value) {
        var index = this.indexByValue(value)
        if (index != -1) {
          this.selectedIndex = index
          this.setLabel(this.items[index].label)
          this.value = this.items[index].value
        } else {
          this.selectedIndex = -1
          this.setLabel(this.getLabelForUnknowValue(value))
          this.value = value
        }
      },
      setLabel: function(label) {
        this.getDom('button_body').innerHTML = label
        this.label = label
      },
      getLabelForUnknowValue: function(value) {
        return value
      },
      indexByValue: function(value) {
        for (var i = 0; i < this.items.length; i++) {
          if (value == this.items[i].value) {
            return i
          }
        }
        return -1
      },
      getItem: function(index) {
        return this.items[index]
      },
      selectByIndex: function(index) {
        if (index < this.items.length && this.fireEvent('select', index) !== false) {
          this.selectedIndex = index
          this.value = this.items[index].value
          this.setLabel(this.items[index].label)
        }
      }
    }
    utils.inherits(Combox, SplitButton)
  })()
  ;(function() {
    var utils = baidu.editor.utils
    var domUtils = baidu.editor.dom.domUtils
    var uiUtils = baidu.editor.ui.uiUtils
    var Mask = baidu.editor.ui.Mask
    var UIBase = baidu.editor.ui.UIBase
    var Button = baidu.editor.ui.Button
    var Dialog = (baidu.editor.ui.Dialog = function(options) {
      if (options.name) {
        var name = options.name
        var cssRules = options.cssRules
        if (!options.className) {
          options.className = 'edui-for-' + name
        }
        if (cssRules) {
          options.cssRules = '.edui-default .edui-for-' + name + ' .edui-dialog-content  {' + cssRules + '}'
        }
      }
      this.initOptions(
        utils.extend(
          {
            autoReset: true,
            draggable: true,
            onok: function() {
            },
            oncancel: function() {
            },
            onclose: function(t, ok) {
              return ok ? this.onok() : this.oncancel()
            },
            holdScroll: false
          },
          options
        )
      )
      this.initDialog()
    })
    var modalMask
    var dragMask
    var activeDialog
    Dialog.prototype = {
      draggable: false,
      uiName: 'dialog',
      initDialog: function() {
        var me = this
        var theme = this.editor.options.theme
        if (this.cssRules) {
          utils.cssRule('edui-customize-' + this.name + '-style', this.cssRules)
        }
        this.initUIBase()
        this.modalMask =
          modalMask ||
          (modalMask = new Mask({
            className: 'edui-dialog-modalmask',
            theme: theme,
            onclick: function() {
              activeDialog && activeDialog.close(false)
            }
          }))
        this.dragMask = dragMask || (dragMask = new Mask({ className: 'edui-dialog-dragmask', theme: theme }))
        this.closeButton = new Button({
          className: 'edui-dialog-closebutton',
          title: me.closeDialog,
          theme: theme,
          onclick: function() {
            me.close(false)
          }
        })
        this.fullscreen && this.initResizeEvent()
        if (this.buttons) {
          for (var i = 0; i < this.buttons.length; i++) {
            if (!(this.buttons[i] instanceof Button)) {
              this.buttons[i] = new Button(utils.extend(this.buttons[i], { editor: this.editor }, true))
            }
          }
        }
      },
      initResizeEvent: function() {
        var me = this
        domUtils.on(window, 'resize', function() {
          if (me._hidden || me._hidden === undefined) {
            return
          }
          if (me.__resizeTimer) {
            window.clearTimeout(me.__resizeTimer)
          }
          me.__resizeTimer = window.setTimeout(function() {
            me.__resizeTimer = null
            var dialogWrapNode = me.getDom()
            var contentNode = me.getDom('content')
            var wrapRect = UE.ui.uiUtils.getClientRect(dialogWrapNode)
            var contentRect = UE.ui.uiUtils.getClientRect(contentNode)
            var vpRect = uiUtils.getViewportRect()
            contentNode.style.width = vpRect.width - wrapRect.width + contentRect.width + 'px'
            contentNode.style.height = vpRect.height - wrapRect.height + contentRect.height + 'px'
            dialogWrapNode.style.width = vpRect.width + 'px'
            dialogWrapNode.style.height = vpRect.height + 'px'
            me.fireEvent('resize')
          }, 100)
        })
      },
      fitSize: function() {
        var popBodyEl = this.getDom('body')
        var size = this.mesureSize()
        popBodyEl.style.width = size.width + 'px'
        popBodyEl.style.height = size.height + 'px'
        return size
      },
      safeSetOffset: function(offset) {
        var me = this
        var el = me.getDom()
        var vpRect = uiUtils.getViewportRect()
        var rect = uiUtils.getClientRect(el)
        var left = offset.left
        if (left + rect.width > vpRect.right) {
          left = vpRect.right - rect.width
        }
        var top = offset.top
        if (top + rect.height > vpRect.bottom) {
          top = vpRect.bottom - rect.height
        }
        el.style.left = Math.max(left, 0) + 'px'
        el.style.top = Math.max(top, 0) + 'px'
      },
      showAtCenter: function() {
        var vpRect = uiUtils.getViewportRect()
        if (!this.fullscreen) {
          this.getDom().style.display = ''
          var popSize = this.fitSize()
          var titleHeight = this.getDom('titlebar').offsetHeight | 0
          var left = vpRect.width / 2 - popSize.width / 2
          var top = vpRect.height / 2 - (popSize.height - titleHeight) / 2 - titleHeight
          var popEl = this.getDom()
          this.safeSetOffset({ left: Math.max(left | 0, 0), top: Math.max(top | 0, 0) })
          if (!domUtils.hasClass(popEl, 'edui-state-centered')) {
            popEl.className += ' edui-state-centered'
          }
        } else {
          var dialogWrapNode = this.getDom()
          var contentNode = this.getDom('content')
          dialogWrapNode.style.display = 'block'
          var wrapRect = UE.ui.uiUtils.getClientRect(dialogWrapNode)
          var contentRect = UE.ui.uiUtils.getClientRect(contentNode)
          dialogWrapNode.style.left = '-100000px'
          contentNode.style.width = vpRect.width - wrapRect.width + contentRect.width + 'px'
          contentNode.style.height = vpRect.height - wrapRect.height + contentRect.height + 'px'
          dialogWrapNode.style.width = vpRect.width + 'px'
          dialogWrapNode.style.height = vpRect.height + 'px'
          dialogWrapNode.style.left = 0
          this._originalContext = {
            html: {
              overflowX: document.documentElement.style.overflowX,
              overflowY: document.documentElement.style.overflowY
            },
            body: { overflowX: document.body.style.overflowX, overflowY: document.body.style.overflowY }
          }
          document.documentElement.style.overflowX = 'hidden'
          document.documentElement.style.overflowY = 'hidden'
          document.body.style.overflowX = 'hidden'
          document.body.style.overflowY = 'hidden'
        }
        this._show()
      },
      getContentHtml: function() {
        var contentHtml = ''
        if (typeof this.content === 'string') {
          contentHtml = this.content
        } else {
          if (this.iframeUrl) {
            contentHtml =
              '<span id="' +
              this.id +
              '_contmask" class="dialogcontmask"></span><iframe id="' +
              this.id +
              '_iframe" class="%%-iframe" height="100%" width="100%" frameborder="0" src="' +
              this.iframeUrl +
              '"></iframe>'
          }
        }
        return contentHtml
      },
      getHtmlTpl: function() {
        var footHtml = ''
        if (this.buttons) {
          var buff = []
          for (var i = 0; i < this.buttons.length; i++) {
            buff[i] = this.buttons[i].renderHtml()
          }
          footHtml =
            '<div class="%%-foot">' + '<div id="##_buttons" class="%%-buttons">' + buff.join('') + '</div>' + '</div>'
        }
        return (
          '<div id="##" class="%%"><div ' +
          (!this.fullscreen ? 'class="%%"' : 'class="%%-wrap edui-dialog-fullscreen-flag"') +
          '><div id="##_body" class="%%-body">' +
          '<div class="%%-shadow"></div>' +
          '<div id="##_titlebar" class="%%-titlebar">' +
          '<div class="%%-draghandle" onmousedown="$$._onTitlebarMouseDown(event, this);">' +
          '<span class="%%-caption">' +
          (this.title || '') +
          '</span>' +
          '</div>' +
          this.closeButton.renderHtml() +
          '</div>' +
          '<div id="##_content" class="%%-content">' +
          (this.autoReset ? '' : this.getContentHtml()) +
          '</div>' +
          footHtml +
          '</div></div></div>'
        )
      },
      postRender: function() {
        if (!this.modalMask.getDom()) {
          this.modalMask.render()
          this.modalMask.hide()
        }
        if (!this.dragMask.getDom()) {
          this.dragMask.render()
          this.dragMask.hide()
        }
        var me = this
        this.addListener('show', function() {
          me.modalMask.show(this.getDom().style.zIndex - 2)
        })
        this.addListener('hide', function() {
          me.modalMask.hide()
        })
        if (this.buttons) {
          for (var i = 0; i < this.buttons.length; i++) {
            this.buttons[i].postRender()
          }
        }
        domUtils.on(window, 'resize', function() {
          setTimeout(function() {
            if (!me.isHidden()) {
              me.safeSetOffset(uiUtils.getClientRect(me.getDom()))
            }
          })
        })
        this._hide()
      },
      mesureSize: function() {
        var body = this.getDom('body')
        var width = uiUtils.getClientRect(this.getDom('content')).width
        var dialogBodyStyle = body.style
        dialogBodyStyle.width = width
        return uiUtils.getClientRect(body)
      },
      _onTitlebarMouseDown: function(evt, el) {
        if (this.draggable) {
          var rect
          var vpRect = uiUtils.getViewportRect()
          var me = this
          uiUtils.startDrag(evt, {
            ondragstart: function() {
              rect = uiUtils.getClientRect(me.getDom())
              me.getDom('contmask').style.visibility = 'visible'
              me.dragMask.show(me.getDom().style.zIndex - 1)
            },
            ondragmove: function(x, y) {
              var left = rect.left + x
              var top = rect.top + y
              me.safeSetOffset({ left: left, top: top })
            },
            ondragstop: function() {
              me.getDom('contmask').style.visibility = 'hidden'
              domUtils.removeClasses(me.getDom(), ['edui-state-centered'])
              me.dragMask.hide()
            }
          })
        }
      },
      reset: function() {
        this.getDom('content').innerHTML = this.getContentHtml()
        this.fireEvent('dialogafterreset')
      },
      _show: function() {
        if (this._hidden) {
          this.getDom().style.display = ''
          this.editor.container.style.zIndex &&
          (this.getDom().style.zIndex = this.editor.container.style.zIndex * 1 + 10)
          this._hidden = false
          this.fireEvent('show')
          baidu.editor.ui.uiUtils.getFixedLayer().style.zIndex = this.getDom().style.zIndex - 4
        }
      },
      isHidden: function() {
        return this._hidden
      },
      _hide: function() {
        if (!this._hidden) {
          var wrapNode = this.getDom()
          wrapNode.style.display = 'none'
          wrapNode.style.zIndex = ''
          wrapNode.style.width = ''
          wrapNode.style.height = ''
          this._hidden = true
          this.fireEvent('hide')
        }
      },
      open: function() {
        if (this.autoReset) {
          try {
            this.reset()
          } catch (e) {
            this.render()
            this.open()
          }
        }
        this.showAtCenter()
        if (this.iframeUrl) {
          try {
            this.getDom('iframe').focus()
          } catch (ex) {
          }
        }
        activeDialog = this
      },
      _onCloseButtonClick: function(evt, el) {
        this.close(false)
      },
      close: function(ok) {
        if (this.fireEvent('close', ok) !== false) {
          if (this.fullscreen) {
            document.documentElement.style.overflowX = this._originalContext.html.overflowX
            document.documentElement.style.overflowY = this._originalContext.html.overflowY
            document.body.style.overflowX = this._originalContext.body.overflowX
            document.body.style.overflowY = this._originalContext.body.overflowY
            delete this._originalContext
          }
          this._hide()
          var content = this.getDom('content')
          var iframe = this.getDom('iframe')
          if (content && iframe) {
            var doc = iframe.contentDocument || iframe.contentWindow.document
            doc && (doc.body.innerHTML = '')
            domUtils.remove(content)
          }
        }
      }
    }
    utils.inherits(Dialog, UIBase)
  })()
  ;(function() {
    var utils = baidu.editor.utils
    var Menu = baidu.editor.ui.Menu
    var SplitButton = baidu.editor.ui.SplitButton
    var MenuButton = (baidu.editor.ui.MenuButton = function(options) {
      this.initOptions(options)
      this.initMenuButton()
    })
    MenuButton.prototype = {
      initMenuButton: function() {
        var me = this
        this.uiName = 'menubutton'
        this.popup = new Menu({ items: me.items, className: me.className, editor: me.editor })
        this.popup.addListener('show', function() {
          var list = this
          for (var i = 0; i < list.items.length; i++) {
            list.items[i].removeState('checked')
            if (list.items[i].value == me._value) {
              list.items[i].addState('checked')
              this.value = me._value
            }
          }
        })
        this.initSplitButton()
      },
      setValue: function(value) {
        this._value = value
      }
    }
    utils.inherits(MenuButton, SplitButton)
  })()
  ;(function() {
    var utils = baidu.editor.utils
    var Popup = baidu.editor.ui.Popup
    var SplitButton = baidu.editor.ui.SplitButton
    var MultiMenuPop = (baidu.editor.ui.MultiMenuPop = function(options) {
      this.initOptions(options)
      this.initMultiMenu()
    })
    MultiMenuPop.prototype = {
      initMultiMenu: function() {
        var me = this
        this.popup = new Popup({
          content: '',
          editor: me.editor,
          iframe_rendered: false,
          onshow: function() {
            if (!this.iframe_rendered) {
              this.iframe_rendered = true
              this.getDom('content').innerHTML =
                '<iframe id="' + me.id + '_iframe" src="' + me.iframeUrl + '" frameborder="0"></iframe>'
              me.editor.container.style.zIndex &&
              (this.getDom().style.zIndex = me.editor.container.style.zIndex * 1 + 1)
            }
          }
        })
        this.onbuttonclick = function() {
          this.showPopup()
        }
        this.initSplitButton()
      }
    }
    utils.inherits(MultiMenuPop, SplitButton)
  })()
  ;(function() {
    var UI = baidu.editor.ui
    var UIBase = UI.UIBase
    var uiUtils = UI.uiUtils
    var utils = baidu.editor.utils
    var domUtils = baidu.editor.dom.domUtils
    var allMenus = []
    var timeID
    var isSubMenuShow = false
    var ShortCutMenu = (UI.ShortCutMenu = function(options) {
      this.initOptions(options)
      this.initShortCutMenu()
    })
    ShortCutMenu.postHide = hideAllMenu
    ShortCutMenu.prototype = {
      isHidden: true,
      SPACE: 5,
      initShortCutMenu: function() {
        this.items = this.items || []
        this.initUIBase()
        this.initItems()
        this.initEvent()
        allMenus.push(this)
      },
      initEvent: function() {
        var me = this
        var doc = me.editor.document
        domUtils.on(doc, 'mousemove', function(e) {
          if (me.isHidden === false) {
            if (me.getSubMenuMark() || me.eventType == 'contextmenu') {
              return
            }
            var flag = true
            var el = me.getDom()
            var wt = el.offsetWidth
            var ht = el.offsetHeight
            var distanceX = wt / 2 + me.SPACE
            var distanceY = ht / 2
            var x = Math.abs(e.screenX - me.left)
            var y = Math.abs(e.screenY - me.top)
            clearTimeout(timeID)
            timeID = setTimeout(function() {
              if (y > 0 && y < distanceY) {
                me.setOpacity(el, '1')
              } else {
                if (y > distanceY && y < distanceY + 70) {
                  me.setOpacity(el, '0.5')
                  flag = false
                } else {
                  if (y > distanceY + 70 && y < distanceY + 140) {
                    me.hide()
                  }
                }
              }
              if (flag && x > 0 && x < distanceX) {
                me.setOpacity(el, '1')
              } else {
                if (x > distanceX && x < distanceX + 70) {
                  me.setOpacity(el, '0.5')
                } else {
                  if (x > distanceX + 70 && x < distanceX + 140) {
                    me.hide()
                  }
                }
              }
            })
          }
        })
        if (browser.chrome) {
          domUtils.on(doc, 'mouseout', function(e) {
            var relatedTgt = e.relatedTarget || e.toElement
            if (relatedTgt == null || relatedTgt.tagName == 'HTML') {
              me.hide()
            }
          })
        }
        me.editor.addListener('afterhidepop', function() {
          if (!me.isHidden) {
            isSubMenuShow = true
          }
        })
      },
      initItems: function() {
        if (utils.isArray(this.items)) {
          for (var i = 0, len = this.items.length; i < len; i++) {
            var item = this.items[i].toLowerCase()
            if (UI[item]) {
              this.items[i] = new UI[item](this.editor)
              this.items[i].className += ' edui-shortcutsubmenu '
            }
          }
        }
      },
      setOpacity: function(el, value) {
        if (browser.ie && browser.version < 9) {
          el.style.filter = 'alpha(opacity = ' + parseFloat(value) * 100 + ');'
        } else {
          el.style.opacity = value
        }
      },
      getSubMenuMark: function() {
        isSubMenuShow = false
        var layerEle = uiUtils.getFixedLayer()
        var list = domUtils.getElementsByTagName(layerEle, 'div', function(node) {
          return domUtils.hasClass(node, 'edui-shortcutsubmenu edui-popup')
        })
        for (var i = 0, node; (node = list[i++]);) {
          if (node.style.display != 'none') {
            isSubMenuShow = true
          }
        }
        return isSubMenuShow
      },
      show: function(e, hasContextmenu) {
        var me = this
        var offset = {}
        var el = this.getDom()
        var fixedlayer = uiUtils.getFixedLayer()

        function setPos(offset) {
          if (offset.left < 0) {
            offset.left = 0
          }
          if (offset.top < 0) {
            offset.top = 0
          }
          el.style.cssText = 'position:absolute;left:' + offset.left + 'px;top:' + offset.top + 'px;'
        }

        function setPosByCxtMenu(menu) {
          if (!menu.tagName) {
            menu = menu.getDom()
          }
          offset.left = parseInt(menu.style.left)
          offset.top = parseInt(menu.style.top)
          offset.top -= el.offsetHeight + 15
          setPos(offset)
        }

        me.eventType = e.type
        el.style.cssText = 'display:block;left:-9999px'
        if (e.type == 'contextmenu' && hasContextmenu) {
          var menu = domUtils.getElementsByTagName(fixedlayer, 'div', 'edui-contextmenu')[0]
          if (menu) {
            setPosByCxtMenu(menu)
          } else {
            me.editor.addListener('aftershowcontextmenu', function(type, menu) {
              setPosByCxtMenu(menu)
            })
          }
        } else {
          offset = uiUtils.getViewportOffsetByEvent(e)
          offset.top -= el.offsetHeight + me.SPACE
          offset.left += me.SPACE + 20
          setPos(offset)
          me.setOpacity(el, 0.2)
        }
        me.isHidden = false
        me.left = e.screenX + el.offsetWidth / 2 - me.SPACE
        me.top = e.screenY - el.offsetHeight / 2 - me.SPACE
        if (me.editor) {
          el.style.zIndex = me.editor.container.style.zIndex * 1 + 10
          fixedlayer.style.zIndex = el.style.zIndex - 1
        }
      },
      hide: function() {
        if (this.getDom()) {
          this.getDom().style.display = 'none'
        }
        this.isHidden = true
      },
      postRender: function() {
        if (utils.isArray(this.items)) {
          for (var i = 0, item; (item = this.items[i++]);) {
            item.postRender()
          }
        }
      },
      getHtmlTpl: function() {
        var buff
        if (utils.isArray(this.items)) {
          buff = []
          for (var i = 0; i < this.items.length; i++) {
            buff[i] = this.items[i].renderHtml()
          }
          buff = buff.join('')
        } else {
          buff = this.items
        }
        return (
          '<div id="##" class="%% edui-toolbar" data-src="shortcutmenu" onmousedown="return false;" onselectstart="return false;" >' +
          buff +
          '</div>'
        )
      }
    }
    utils.inherits(ShortCutMenu, UIBase)

    function hideAllMenu(e) {
      var tgt = e.target || e.srcElement
      var cur = domUtils.findParent(
        tgt,
        function(node) {
          return domUtils.hasClass(node, 'edui-shortcutmenu') || domUtils.hasClass(node, 'edui-popup')
        },
        true
      )
      if (!cur) {
        for (var i = 0, menu; (menu = allMenus[i++]);) {
          menu.hide()
        }
      }
    }

    domUtils.on(document, 'mousedown', function(e) {
      hideAllMenu(e)
    })
    domUtils.on(window, 'scroll', function(e) {
      hideAllMenu(e)
    })
  })()
  ;(function() {
    var utils = baidu.editor.utils
    var UIBase = baidu.editor.ui.UIBase
    var Breakline = (baidu.editor.ui.Breakline = function(options) {
      this.initOptions(options)
      this.initSeparator()
    })
    Breakline.prototype = {
      uiName: 'Breakline',
      initSeparator: function() {
        this.initUIBase()
      },
      getHtmlTpl: function() {
        return '<br/>'
      }
    }
    utils.inherits(Breakline, UIBase)
  })()
  ;(function() {
    var utils = baidu.editor.utils
    var domUtils = baidu.editor.dom.domUtils
    var UIBase = baidu.editor.ui.UIBase
    var Message = (baidu.editor.ui.Message = function(options) {
      this.initOptions(options)
      this.initMessage()
    })
    Message.prototype = {
      initMessage: function() {
        this.initUIBase()
      },
      getHtmlTpl: function() {
        return (
          '<div id="##" class="edui-message %%">' +
          ' <div id="##_closer" class="edui-message-closer">×</div>' +
          ' <div id="##_body" class="edui-message-body edui-message-type-info">' +
          ' <iframe style="position:absolute;z-index:-1;left:0;top:0;background-color: transparent;" frameborder="0" width="100%" height="100%" src="about:blank"></iframe>' +
          ' <div class="edui-shadow"></div>' +
          ' <div id="##_content" class="edui-message-content">' +
          '  </div>' +
          ' </div>' +
          '</div>'
        )
      },
      reset: function(opt) {
        var me = this
        if (!opt.keepshow) {
          clearTimeout(this.timer)
          me.timer = setTimeout(function() {
            me.hide()
          }, opt.timeout || 4000)
        }
        opt.content !== undefined && me.setContent(opt.content)
        opt.type !== undefined && me.setType(opt.type)
        me.show()
      },
      postRender: function() {
        var me = this
        var closer = this.getDom('closer')
        closer &&
        domUtils.on(closer, 'click', function() {
          me.hide()
        })
      },
      setContent: function(content) {
        this.getDom('content').innerHTML = content
      },
      setType: function(type) {
        type = type || 'info'
        var body = this.getDom('body')
        body.className = body.className.replace(/edui-message-type-[\w-]+/, 'edui-message-type-' + type)
      },
      getContent: function() {
        return this.getDom('content').innerHTML
      },
      getType: function() {
        var arr = this.getDom('body').match(/edui-message-type-([\w-]+)/)
        return arr ? arr[1] : ''
      },
      show: function() {
        this.getDom().style.display = 'block'
      },
      hide: function() {
        var dom = this.getDom()
        if (dom) {
          dom.style.display = 'none'
          dom.parentNode && dom.parentNode.removeChild(dom)
        }
      }
    }
    utils.inherits(Message, UIBase)
  })()
  ;(function() {
    var utils = baidu.editor.utils
    var editorui = baidu.editor.ui
    var _Dialog = editorui.Dialog
    editorui.buttons = {}
    editorui.Dialog = function(options) {
      var dialog = new _Dialog(options)
      dialog.addListener('hide', function() {
        if (dialog.editor) {
          var editor = dialog.editor
          try {
            if (browser.gecko) {
              var y = editor.window.scrollY
              var x = editor.window.scrollX
              editor.body.focus()
              editor.window.scrollTo(x, y)
            } else {
              editor.focus()
            }
          } catch (ex) {
          }
        }
      })
      return dialog
    }
    var iframeUrlMap = {
      anchor: '~/dialogs/anchor/anchor.html',
      insertimage: '~/dialogs/image/image.html',
      link: '~/dialogs/link/link.html',
      spechars: '~/dialogs/spechars/spechars.html',
      searchreplace: '~/dialogs/searchreplace/searchreplace.html',
      map: '~/dialogs/map/map.html',
      gmap: '~/dialogs/gmap/gmap.html',
      insertvideo: '~/dialogs/video/video.html',
      help: '~/dialogs/help/help.html',
      preview: '~/dialogs/preview/preview.html',
      emotion: '~/dialogs/emotion/emotion.html',
      wordimage: '~/dialogs/wordimage/wordimage.html',
      attachment: '~/dialogs/attachment/attachment.html',
      insertframe: '~/dialogs/insertframe/insertframe.html',
      edittip: '~/dialogs/table/edittip.html',
      edittable: '~/dialogs/table/edittable.html',
      edittd: '~/dialogs/table/edittd.html',
      webapp: '~/dialogs/webapp/webapp.html',
      snapscreen: '~/dialogs/snapscreen/snapscreen.html',
      scrawl: '~/dialogs/scrawl/scrawl.html',
      music: '~/dialogs/music/music.html',
      template: '~/dialogs/template/template.html',
      background: '~/dialogs/background/background.html',
      charts: '~/dialogs/charts/charts.html'
    }
    var btnCmds = [
      'undo',
      'redo',
      'formatmatch',
      'bold',
      'italic',
      'underline',
      'fontborder',
      'touppercase',
      'tolowercase',
      'strikethrough',
      'subscript',
      'superscript',
      'source',
      'indent',
      'outdent',
      'blockquote',
      'pasteplain',
      'pagebreak',
      'selectall',
      'print',
      'horizontal',
      'removeformat',
      'time',
      'date',
      'unlink',
      'insertparagraphbeforetable',
      'insertrow',
      'insertcol',
      'mergeright',
      'mergedown',
      'deleterow',
      'deletecol',
      'splittorows',
      'splittocols',
      'splittocells',
      'mergecells',
      'deletetable',
      'drafts'
    ]
    for (var i = 0, ci; (ci = btnCmds[i++]);) {
      ci = ci.toLowerCase()
      editorui[ci] = (function(cmd) {
        return function(editor) {
          var ui = new editorui.Button({
            className: 'edui-for-' + cmd,
            title: editor.options.labelMap[cmd] || editor.getLang('labelMap.' + cmd) || '',
            onclick: function() {
              editor.execCommand(cmd)
            },
            theme: editor.options.theme,
            showText: false
          })
          editorui.buttons[cmd] = ui
          editor.addListener('selectionchange', function(type, causeByUi, uiReady) {
            var state = editor.queryCommandState(cmd)
            if (state == -1) {
              ui.setDisabled(true)
              ui.setChecked(false)
            } else {
              if (!uiReady) {
                ui.setDisabled(false)
                ui.setChecked(state)
              }
            }
          })
          return ui
        }
      })(ci)
    }
    editorui.cleardoc = function(editor) {
      var ui = new editorui.Button({
        className: 'edui-for-cleardoc',
        title: editor.options.labelMap.cleardoc || editor.getLang('labelMap.cleardoc') || '',
        theme: editor.options.theme,
        onclick: function() {
          if (confirm(editor.getLang('confirmClear'))) {
            editor.execCommand('cleardoc')
          }
        }
      })
      editorui.buttons['cleardoc'] = ui
      editor.addListener('selectionchange', function() {
        ui.setDisabled(editor.queryCommandState('cleardoc') == -1)
      })
      return ui
    }
    editorui.fullangledetection = function(editor) {
      var ui = new editorui.Button({
        className: 'edui-for-fullangledetection',
        title: editor.options.labelMap.cleardoc || editor.getLang('labelMap.fullangledetection') || '',
        theme: editor.options.theme,
        onclick: function() {
          // if (confirm(editor.getLang('confirmClear'))) {
          editor.execCommand('fullangledetection')
          // }
        }
      })
      editorui.buttons['fullangledetection'] = ui
      editor.addListener('selectionchange', function() {
        ui.setDisabled(editor.queryCommandState('fullangledetection') == -1)
      })
      return ui
    }

    editorui.formatstandard = function(editor) {
      var ui = new editorui.Button({
        className: 'edui-for-formatstandard',
        title: editor.options.labelMap.cleardoc || editor.getLang('labelMap.formatstandard') || '',
        theme: editor.options.theme,
        onclick: function() {
          // if (confirm(editor.getLang('confirmClear'))) {
          editor.execCommand('formatstandard')
          // }
        }
      })
      editorui.buttons['formatstandard'] = ui
      editor.addListener('selectionchange', function() {
        ui.setDisabled(editor.queryCommandState('formatstandard') == -1)
      })
      return ui
    }
    var typeset = {
      justify: ['left', 'right', 'center', 'justify'],
      imagefloat: ['none', 'left', 'center', 'right'],
      directionality: ['ltr', 'rtl']
    }
    for (var p in typeset) {
      ;(function(cmd, val) {
        for (var i = 0, ci; (ci = val[i++]);) {
          ;(function(cmd2) {
            editorui[cmd.replace('float', '') + cmd2] = function(editor) {
              var ui = new editorui.Button({
                className: 'edui-for-' + cmd.replace('float', '') + cmd2,
                title:
                  editor.options.labelMap[cmd.replace('float', '') + cmd2] ||
                  editor.getLang('labelMap.' + cmd.replace('float', '') + cmd2) ||
                  '',
                theme: editor.options.theme,
                onclick: function() {
                  editor.execCommand(cmd, cmd2)
                }
              })
              editorui.buttons[cmd] = ui
              editor.addListener('selectionchange', function(type, causeByUi, uiReady) {
                ui.setDisabled(editor.queryCommandState(cmd) == -1)
                ui.setChecked(editor.queryCommandValue(cmd) == cmd2 && !uiReady)
              })
              return ui
            }
          })(ci)
        }
      })(p, typeset[p])
    }
    for (var i = 0, ci; (ci = ['backcolor', 'forecolor'][i++]);) {
      editorui[ci] = (function(cmd) {
        return function(editor) {
          var ui = new editorui.ColorButton({
            className: 'edui-for-' + cmd,
            color: 'default',
            title: editor.options.labelMap[cmd] || editor.getLang('labelMap.' + cmd) || '',
            editor: editor,
            onpickcolor: function(t, color) {
              editor.execCommand(cmd, color)
            },
            onpicknocolor: function() {
              editor.execCommand(cmd, 'default')
              this.setColor('transparent')
              this.color = 'default'
            },
            onbuttonclick: function() {
              editor.execCommand(cmd, this.color)
            }
          })
          editorui.buttons[cmd] = ui
          editor.addListener('selectionchange', function() {
            ui.setDisabled(editor.queryCommandState(cmd) == -1)
          })
          return ui
        }
      })(ci)
    }
    var dialogBtns = {
      noOk: ['searchreplace', 'help', 'spechars', 'webapp', 'preview'],
      ok: [
        'attachment',
        'anchor',
        'link',
        'insertimage',
        'map',
        'gmap',
        'insertframe',
        'wordimage',
        'insertvideo',
        'insertframe',
        'edittip',
        'edittable',
        'edittd',
        'scrawl',
        'template',
        'music',
        'background',
        'charts'
      ]
    }
    for (var p in dialogBtns) {
      ;(function(type, vals) {
        for (var i = 0, ci; (ci = vals[i++]);) {
          if (browser.opera && ci === 'searchreplace') {
            continue
          }
          ;(function(cmd) {
            editorui[cmd] = function(editor, iframeUrl, title) {
              iframeUrl = iframeUrl || (editor.options.iframeUrlMap || {})[cmd] || iframeUrlMap[cmd]
              title = editor.options.labelMap[cmd] || editor.getLang('labelMap.' + cmd) || ''
              var dialog
              if (iframeUrl) {
                dialog = new editorui.Dialog(
                  utils.extend(
                    {
                      iframeUrl: editor.ui.mapUrl(iframeUrl),
                      editor: editor,
                      className: 'edui-for-' + cmd,
                      title: title,
                      holdScroll: cmd === 'insertimage',
                      fullscreen: /charts|preview/.test(cmd),
                      closeDialog: editor.getLang('closeDialog')
                    },
                    type == 'ok'
                      ? {
                        buttons: [
                          {
                            className: 'edui-okbutton',
                            label: editor.getLang('ok'),
                            editor: editor,
                            onclick: function() {
                              dialog.close(true)
                            }
                          },
                          {
                            className: 'edui-cancelbutton',
                            label: editor.getLang('cancel'),
                            editor: editor,
                            onclick: function() {
                              dialog.close(false)
                            }
                          }
                        ]
                      }
                      : {}
                  )
                )
                editor.ui._dialogs[cmd + 'Dialog'] = dialog
              }
              var ui = new editorui.Button({
                className: 'edui-for-' + cmd,
                title: title,
                onclick: function() {
                  if (dialog) {
                    switch (cmd) {
                      case 'wordimage':
                        var images = editor.execCommand('wordimage')
                        if (images && images.length) {
                          dialog.render()
                          dialog.open()
                        }
                        break
                      case 'scrawl':
                        if (editor.queryCommandState('scrawl') != -1) {
                          dialog.render()
                          dialog.open()
                        }
                        break
                      default:
                        dialog.render()
                        dialog.open()
                    }
                  }
                },
                theme: editor.options.theme,
                disabled: (cmd == 'scrawl' && editor.queryCommandState('scrawl') == -1) || cmd == 'charts'
              })
              editorui.buttons[cmd] = ui
              editor.addListener('selectionchange', function() {
                var unNeedCheckState = { edittable: 1 }
                if (cmd in unNeedCheckState) {
                  return
                }
                var state = editor.queryCommandState(cmd)
                if (ui.getDom()) {
                  ui.setDisabled(state == -1)
                  ui.setChecked(state)
                }
              })
              return ui
            }
          })(ci.toLowerCase())
        }
      })(p, dialogBtns[p])
    }
    editorui.snapscreen = function(editor, iframeUrl, title) {
      title = editor.options.labelMap['snapscreen'] || editor.getLang('labelMap.snapscreen') || ''
      var ui = new editorui.Button({
        className: 'edui-for-snapscreen',
        title: title,
        onclick: function() {
          editor.execCommand('snapscreen')
        },
        theme: editor.options.theme
      })
      editorui.buttons['snapscreen'] = ui
      iframeUrl = iframeUrl || (editor.options.iframeUrlMap || {})['snapscreen'] || iframeUrlMap['snapscreen']
      if (iframeUrl) {
        var dialog = new editorui.Dialog({
          iframeUrl: editor.ui.mapUrl(iframeUrl),
          editor: editor,
          className: 'edui-for-snapscreen',
          title: title,
          buttons: [
            {
              className: 'edui-okbutton',
              label: editor.getLang('ok'),
              editor: editor,
              onclick: function() {
                dialog.close(true)
              }
            },
            {
              className: 'edui-cancelbutton',
              label: editor.getLang('cancel'),
              editor: editor,
              onclick: function() {
                dialog.close(false)
              }
            }
          ]
        })
        dialog.render()
        editor.ui._dialogs['snapscreenDialog'] = dialog
      }
      editor.addListener('selectionchange', function() {
        ui.setDisabled(editor.queryCommandState('snapscreen') == -1)
      })
      return ui
    }
    editorui.insertcode = function(editor, list, title) {
      list = editor.options['insertcode'] || []
      title = editor.options.labelMap['insertcode'] || editor.getLang('labelMap.insertcode') || ''
      var items = []
      utils.each(list, function(key, val) {
        items.push({
          label: key,
          value: val,
          theme: editor.options.theme,
          renderLabelHtml: function() {
            return '<div class="edui-label %%-label" >' + (this.label || '') + '</div>'
          }
        })
      })
      var ui = new editorui.Combox({
        editor: editor,
        items: items,
        onselect: function(t, index) {
          editor.execCommand('insertcode', this.items[index].value)
        },
        onbuttonclick: function() {
          this.showPopup()
        },
        title: title,
        initValue: title,
        className: 'edui-for-insertcode',
        indexByValue: function(value) {
          if (value) {
            for (var i = 0, ci; (ci = this.items[i]); i++) {
              if (ci.value.indexOf(value) != -1) {
                return i
              }
            }
          }
          return -1
        }
      })
      editorui.buttons['insertcode'] = ui
      editor.addListener('selectionchange', function(type, causeByUi, uiReady) {
        if (!uiReady) {
          var state = editor.queryCommandState('insertcode')
          if (state == -1) {
            ui.setDisabled(true)
          } else {
            ui.setDisabled(false)
            var value = editor.queryCommandValue('insertcode')
            if (!value) {
              ui.setValue(title)
              return
            }
            value && (value = value.replace(/['"]/g, '').split(',')[0])
            ui.setValue(value)
          }
        }
      })
      return ui
    }
    editorui.fontfamily = function(editor, list, title) {
      list = editor.options['fontfamily'] || []
      title = editor.options.labelMap['fontfamily'] || editor.getLang('labelMap.fontfamily') || ''
      if (!list.length) {
        return
      }
      for (var i = 0, ci, items = []; (ci = list[i]); i++) {
        var langLabel = editor.getLang('fontfamily')[ci.name] || ''
        ;(function(key, val) {
          items.push({
            label: key,
            value: val,
            theme: editor.options.theme,
            renderLabelHtml: function() {
              return (
                '<div class="edui-label %%-label" style="font-family:' +
                utils.unhtml(this.value) +
                '">' +
                (this.label || '') +
                '</div>'
              )
            }
          })
        })(ci.label || langLabel, ci.val)
      }
      var ui = new editorui.Combox({
        editor: editor,
        items: items,
        onselect: function(t, index) {
          editor.execCommand('FontFamily', this.items[index].value)
        },
        onbuttonclick: function() {
          this.showPopup()
        },
        title: title,
        initValue: title,
        className: 'edui-for-fontfamily',
        indexByValue: function(value) {
          if (value) {
            for (var i = 0, ci; (ci = this.items[i]); i++) {
              if (ci.value.indexOf(value) != -1) {
                return i
              }
            }
          }
          return -1
        }
      })
      editorui.buttons['fontfamily'] = ui
      editor.addListener('selectionchange', function(type, causeByUi, uiReady) {
        if (!uiReady) {
          var state = editor.queryCommandState('FontFamily')
          if (state == -1) {
            ui.setDisabled(true)
          } else {
            ui.setDisabled(false)
            var value = editor.queryCommandValue('FontFamily')
            value && (value = value.replace(/['"]/g, '').split(',')[0])
            ui.setValue(value)
          }
        }
      })
      return ui
    }
    editorui.fontsize = function(editor, list, title) {
      title = editor.options.labelMap['fontsize'] || editor.getLang('labelMap.fontsize') || ''
      list = list || editor.options['fontsize'] || []
      if (!list.length) {
        return
      }
      var items = []
      for (var i = 0; i < list.length; i++) {
        var size = list[i] + 'px'
        items.push({
          label: size,
          value: size,
          theme: editor.options.theme,
          renderLabelHtml: function() {
            return (
              '<div class="edui-label %%-label" style="line-height:1;font-size:' +
              this.value +
              '">' +
              (this.label || '') +
              '</div>'
            )
          }
        })
      }
      var ui = new editorui.Combox({
        editor: editor,
        items: items,
        title: title,
        initValue: title,
        onselect: function(t, index) {
          editor.execCommand('FontSize', this.items[index].value)
        },
        onbuttonclick: function() {
          this.showPopup()
        },
        className: 'edui-for-fontsize'
      })
      editorui.buttons['fontsize'] = ui
      editor.addListener('selectionchange', function(type, causeByUi, uiReady) {
        if (!uiReady) {
          var state = editor.queryCommandState('FontSize')
          if (state == -1) {
            ui.setDisabled(true)
          } else {
            ui.setDisabled(false)
            ui.setValue(editor.queryCommandValue('FontSize'))
          }
        }
      })
      return ui
    }
    editorui.paragraph = function(editor, list, title) {
      title = editor.options.labelMap['paragraph'] || editor.getLang('labelMap.paragraph') || ''
      list = editor.options['paragraph'] || []
      if (utils.isEmptyObject(list)) {
        return
      }
      var items = []
      for (var i in list) {
        items.push({
          value: i,
          label: list[i] || editor.getLang('paragraph')[i],
          theme: editor.options.theme,
          renderLabelHtml: function() {
            return (
              '<div class="edui-label %%-label"><span class="edui-for-' +
              this.value +
              '">' +
              (this.label || '') +
              '</span></div>'
            )
          }
        })
      }
      var ui = new editorui.Combox({
        editor: editor,
        items: items,
        title: title,
        initValue: title,
        className: 'edui-for-paragraph',
        onselect: function(t, index) {
          editor.execCommand('Paragraph', this.items[index].value)
        },
        onbuttonclick: function() {
          this.showPopup()
        }
      })
      editorui.buttons['paragraph'] = ui
      editor.addListener('selectionchange', function(type, causeByUi, uiReady) {
        if (!uiReady) {
          var state = editor.queryCommandState('Paragraph')
          if (state == -1) {
            ui.setDisabled(true)
          } else {
            ui.setDisabled(false)
            var value = editor.queryCommandValue('Paragraph')
            var index = ui.indexByValue(value)
            if (index != -1) {
              ui.setValue(value)
            } else {
              ui.setValue(ui.initValue)
            }
          }
        }
      })
      return ui
    }
    editorui.customstyle = function(editor) {
      var list = editor.options['customstyle'] || []
      var title = editor.options.labelMap['customstyle'] || editor.getLang('labelMap.customstyle') || ''
      if (!list.length) {
        return
      }
      var langCs = editor.getLang('customstyle')
      for (var i = 0, items = [], t; (t = list[i++]);) {
        ;(function(t) {
          var ck = {}
          ck.label = t.label ? t.label : langCs[t.name]
          ck.style = t.style
          ck.className = t.className
          ck.tag = t.tag
          items.push({
            label: ck.label,
            value: ck,
            theme: editor.options.theme,
            renderLabelHtml: function() {
              return (
                '<div class="edui-label %%-label">' +
                '<' +
                ck.tag +
                ' ' +
                (ck.className ? ' class="' + ck.className + '"' : '') +
                (ck.style ? ' style="' + ck.style + '"' : '') +
                '>' +
                ck.label +
                '</' +
                ck.tag +
                '>' +
                '</div>'
              )
            }
          })
        })(t)
      }
      var ui = new editorui.Combox({
        editor: editor,
        items: items,
        title: title,
        initValue: title,
        className: 'edui-for-customstyle',
        onselect: function(t, index) {
          editor.execCommand('customstyle', this.items[index].value)
        },
        onbuttonclick: function() {
          this.showPopup()
        },
        indexByValue: function(value) {
          for (var i = 0, ti; (ti = this.items[i++]);) {
            if (ti.label == value) {
              return i - 1
            }
          }
          return -1
        }
      })
      editorui.buttons['customstyle'] = ui
      editor.addListener('selectionchange', function(type, causeByUi, uiReady) {
        if (!uiReady) {
          var state = editor.queryCommandState('customstyle')
          if (state == -1) {
            ui.setDisabled(true)
          } else {
            ui.setDisabled(false)
            var value = editor.queryCommandValue('customstyle')
            var index = ui.indexByValue(value)
            if (index != -1) {
              ui.setValue(value)
            } else {
              ui.setValue(ui.initValue)
            }
          }
        }
      })
      return ui
    }
    editorui.inserttable = function(editor, iframeUrl, title) {
      title = editor.options.labelMap['inserttable'] || editor.getLang('labelMap.inserttable') || ''
      var ui = new editorui.TableButton({
        editor: editor,
        title: title,
        className: 'edui-for-inserttable',
        onpicktable: function(t, numCols, numRows) {
          editor.execCommand('InsertTable', { numRows: numRows, numCols: numCols, border: 1 })
        },
        onbuttonclick: function() {
          this.showPopup()
        }
      })
      editorui.buttons['inserttable'] = ui
      editor.addListener('selectionchange', function() {
        ui.setDisabled(editor.queryCommandState('inserttable') == -1)
      })
      return ui
    }
    editorui.lineheight = function(editor) {
      var val = editor.options.lineheight || []
      if (!val.length) {
        return
      }
      for (var i = 0, ci, items = []; (ci = val[i++]);) {
        items.push({
          label: ci,
          value: ci,
          theme: editor.options.theme,
          onclick: function() {
            editor.execCommand('lineheight', this.value)
          }
        })
      }
      var ui = new editorui.MenuButton({
        editor: editor,
        className: 'edui-for-lineheight',
        title: editor.options.labelMap['lineheight'] || editor.getLang('labelMap.lineheight') || '',
        items: items,
        onbuttonclick: function() {
          var value = editor.queryCommandValue('LineHeight') || this.value
          editor.execCommand('LineHeight', value)
        }
      })
      editorui.buttons['lineheight'] = ui
      editor.addListener('selectionchange', function() {
        var state = editor.queryCommandState('LineHeight')
        if (state == -1) {
          ui.setDisabled(true)
        } else {
          ui.setDisabled(false)
          var value = editor.queryCommandValue('LineHeight')
          value && ui.setValue((value + '').replace(/cm/, ''))
          ui.setChecked(state)
        }
      })
      return ui
    }
    editorui.letterspacing = function(editor) {
      var val = editor.options.letterspacing || []
      if (!val.length) {
        return
      }
      for (var j = 0, Li, items = []; j < val.length; j++) {
        ;(Li = '' + val[j]),
          items.push({
            label: Li,
            value: Li,
            theme: editor.options.theme,
            onclick: function() {
              editor.execCommand('letterspacing', this.value)
            }
          })
      }
      var ui = new editorui.MenuButton({
        editor: editor,
        className: 'edui-for-letterspacing',
        title: editor.options.labelMap['letterspacing'] || editor.getLang('labelMap.letterspacing') || '',
        items: items,
        onbuttonclick: function() {
          var value = editor.queryCommandValue('Letterspacing') || this.value
          editor.execCommand('Letterspacing', value)
        }
      })
      editorui.buttons['letterspacing'] = ui
      editor.addListener('selectionchange', function() {
        var state = editor.queryCommandState('Letterspacing')
        if (state == -1) {
          ui.setDisabled(true)
        } else {
          ui.setDisabled(false)
          var value = editor.queryCommandValue('Letterspacing')
          value && ui.setValue((value + '').replace(/cm/, ''))
          ui.setChecked(state)
        }
      })
      return ui
    }
    var rowspacings = ['top', 'bottom']
    for (var r = 0, ri; (ri = rowspacings[r++]);) {
      ;(function(cmd) {
        editorui['rowspacing' + cmd] = function(editor) {
          var val = editor.options['rowspacing' + cmd] || []
          if (!val.length) {
            return null
          }
          for (var i = 0, ci, items = []; (ci = val[i++]);) {
            items.push({
              label: ci,
              value: ci,
              theme: editor.options.theme,
              onclick: function() {
                editor.execCommand('rowspacing', this.value, cmd)
              }
            })
          }
          var ui = new editorui.MenuButton({
            editor: editor,
            className: 'edui-for-rowspacing' + cmd,
            title: editor.options.labelMap['rowspacing' + cmd] || editor.getLang('labelMap.rowspacing' + cmd) || '',
            items: items,
            onbuttonclick: function() {
              var value = editor.queryCommandValue('rowspacing', cmd) || this.value
              editor.execCommand('rowspacing', value, cmd)
            }
          })
          editorui.buttons[cmd] = ui
          editor.addListener('selectionchange', function() {
            var state = editor.queryCommandState('rowspacing', cmd)
            if (state == -1) {
              ui.setDisabled(true)
            } else {
              ui.setDisabled(false)
              var value = editor.queryCommandValue('rowspacing', cmd)
              value && ui.setValue((value + '').replace(/%/, ''))
              ui.setChecked(state)
            }
          })
          return ui
        }
      })(ri)
    }
    var lists = ['insertorderedlist', 'insertunorderedlist']
    for (var l = 0, cl; (cl = lists[l++]);) {
      ;(function(cmd) {
        editorui[cmd] = function(editor) {
          var vals = editor.options[cmd]
          var _onMenuClick = function() {
            editor.execCommand(cmd, this.value)
          }
          var items = []
          for (var i in vals) {
            items.push({
              label: vals[i] || editor.getLang()[cmd][i] || '',
              value: i,
              theme: editor.options.theme,
              onclick: _onMenuClick
            })
          }
          var ui = new editorui.MenuButton({
            editor: editor,
            className: 'edui-for-' + cmd,
            title: editor.getLang('labelMap.' + cmd) || '',
            items: items,
            onbuttonclick: function() {
              var value = editor.queryCommandValue(cmd) || this.value
              editor.execCommand(cmd, value)
            }
          })
          editorui.buttons[cmd] = ui
          editor.addListener('selectionchange', function() {
            var state = editor.queryCommandState(cmd)
            if (state == -1) {
              ui.setDisabled(true)
            } else {
              ui.setDisabled(false)
              var value = editor.queryCommandValue(cmd)
              ui.setValue(value)
              ui.setChecked(state)
            }
          })
          return ui
        }
      })(cl)
    }
    editorui.fullscreen = function(editor, title) {
      title = editor.options.labelMap['fullscreen'] || editor.getLang('labelMap.fullscreen') || ''
      var ui = new editorui.Button({
        className: 'edui-for-fullscreen',
        title: title,
        theme: editor.options.theme,
        onclick: function() {
          if (editor.ui) {
            editor.ui.setFullScreen(!editor.ui.isFullScreen())
          }
          this.setChecked(editor.ui.isFullScreen())
        }
      })
      editorui.buttons['fullscreen'] = ui
      editor.addListener('selectionchange', function() {
        var state = editor.queryCommandState('fullscreen')
        ui.setDisabled(state == -1)
        ui.setChecked(editor.ui.isFullScreen())
      })
      return ui
    }
    editorui['emotion'] = function(editor, iframeUrl) {
      var cmd = 'emotion'
      var ui = new editorui.MultiMenuPop({
        title: editor.options.labelMap[cmd] || editor.getLang('labelMap.' + cmd + '') || '',
        editor: editor,
        className: 'edui-for-' + cmd,
        iframeUrl: editor.ui.mapUrl(iframeUrl || (editor.options.iframeUrlMap || {})[cmd] || iframeUrlMap[cmd])
      })
      editorui.buttons[cmd] = ui
      editor.addListener('selectionchange', function() {
        ui.setDisabled(editor.queryCommandState(cmd) == -1)
      })
      return ui
    }
    editorui.autotypeset = function(editor) {
      var ui = new editorui.AutoTypeSetButton({
        editor: editor,
        title: editor.options.labelMap['autotypeset'] || editor.getLang('labelMap.autotypeset') || '',
        className: 'edui-for-autotypeset',
        onbuttonclick: function() {
          editor.execCommand('autotypeset')
        }
      })
      editorui.buttons['autotypeset'] = ui
      editor.addListener('selectionchange', function() {
        ui.setDisabled(editor.queryCommandState('autotypeset') == -1)
      })
      return ui
    }
    editorui['simpleupload'] = function(editor) {
      var name = 'simpleupload'
      var ui = new editorui.Button({
        className: 'edui-for-' + name,
        title: editor.options.labelMap[name] || editor.getLang('labelMap.' + name) || '',
        onclick: function() {
        },
        theme: editor.options.theme,
        showText: false
      })
      editorui.buttons[name] = ui
      editor.addListener('ready', function() {
        var b = ui.getDom('body')
        var iconSpan = b.children[0]
        editor.fireEvent('simpleuploadbtnready', iconSpan)
      })
      editor.addListener('selectionchange', function(type, causeByUi, uiReady) {
        var state = editor.queryCommandState(name)
        if (state == -1) {
          ui.setDisabled(true)
          ui.setChecked(false)
        } else {
          if (!uiReady) {
            ui.setDisabled(false)
            ui.setChecked(state)
          }
        }
      })
      return ui
    }
  })()
  ;(function() {
    var utils = baidu.editor.utils
    var uiUtils = baidu.editor.ui.uiUtils
    var UIBase = baidu.editor.ui.UIBase
    var domUtils = baidu.editor.dom.domUtils
    var nodeStack = []

    function EditorUI(options) {
      this.initOptions(options)
      this.initEditorUI()
    }

    EditorUI.prototype = {
      uiName: 'editor',
      initEditorUI: function() {
        this.editor.ui = this
        this._dialogs = {}
        this.initUIBase()
        this._initToolbars()
        var editor = this.editor
        var me = this
        editor.addListener('ready', function() {
          editor.getDialog = function(name) {
            return editor.ui._dialogs[name + 'Dialog']
          }
          domUtils.on(editor.window, 'scroll', function(evt) {
            baidu.editor.ui.Popup.postHide(evt)
          })
          editor.ui._actualFrameWidth = editor.options.initialFrameWidth
          UE.browser.ie &&
          UE.browser.version === 6 &&
          editor.container.ownerDocument.execCommand('BackgroundImageCache', false, true)
          if (editor.options.elementPathEnabled) {
            editor.ui.getDom('elementpath').innerHTML =
              '<div class="edui-editor-breadcrumb">' + editor.getLang('elementPathTip') + ':</div>'
          }
          if (editor.options.wordCount) {
            function countFn() {
              setCount(editor, me)
              domUtils.un(editor.document, 'click', arguments.callee)
            }

            domUtils.on(editor.document, 'click', countFn)
            editor.ui.getDom('wordcount').innerHTML = editor.getLang('wordCountTip')
          }
          editor.ui._scale()
          if (editor.options.scaleEnabled) {
            if (editor.autoHeightEnabled) {
              editor.disableAutoHeight()
            }
            me.enableScale()
          } else {
            me.disableScale()
          }
          if (!editor.options.elementPathEnabled && !editor.options.wordCount && !editor.options.scaleEnabled) {
            editor.ui.getDom('elementpath').style.display = 'none'
            editor.ui.getDom('wordcount').style.display = 'none'
            editor.ui.getDom('scale').style.display = 'none'
          }
          if (!editor.selection.isFocus()) {
            return
          }
          editor.fireEvent('selectionchange', false, true)
        })
        editor.addListener('mousedown', function(t, evt) {
          var el = evt.target || evt.srcElement
          baidu.editor.ui.Popup.postHide(evt, el)
          baidu.editor.ui.ShortCutMenu.postHide(evt)
        })
        editor.addListener('delcells', function() {
          if (UE.ui['edittip']) {
            new UE.ui['edittip'](editor)
          }
          editor.getDialog('edittip').open()
        })
        var pastePop
        var isPaste = false
        var timer
        editor.addListener('afterpaste', function() {
          if (editor.queryCommandState('pasteplain')) {
            return
          }
          if (baidu.editor.ui.PastePicker) {
            pastePop = new baidu.editor.ui.Popup({
              content: new baidu.editor.ui.PastePicker({ editor: editor }),
              editor: editor,
              className: 'edui-wordpastepop'
            })
            pastePop.render()
          }
          isPaste = true
        })
        editor.addListener('afterinserthtml', function() {
          clearTimeout(timer)
          timer = setTimeout(function() {
            if (pastePop && (isPaste || editor.ui._isTransfer)) {
              if (pastePop.isHidden()) {
                var span = domUtils.createElement(editor.document, 'span', {
                  style: 'line-height:0px;',
                  innerHTML: '\ufeff'
                })
                var range = editor.selection.getRange()
                range.insertNode(span)
                var tmp = getDomNode(span, 'firstChild', 'previousSibling')
                tmp && pastePop.showAnchor(tmp.nodeType == 3 ? tmp.parentNode : tmp)
                domUtils.remove(span)
              } else {
                pastePop.show()
              }
              delete editor.ui._isTransfer
              isPaste = false
            }
          }, 200)
        })
        editor.addListener('contextmenu', function(t, evt) {
          baidu.editor.ui.Popup.postHide(evt)
        })
        editor.addListener('keydown', function(t, evt) {
          if (pastePop) {
            pastePop.dispose(evt)
          }
          var keyCode = evt.keyCode || evt.which
          if (evt.altKey && keyCode == 90) {
            UE.ui.buttons['fullscreen'].onclick()
          }
        })
        editor.addListener('wordcount', function(type) {
          setCount(this, me)
        })

        function setCount(editor, ui) {
          editor.setOpt({
            wordCount: true,
            maximumWords: 10000,
            wordCountMsg: editor.options.wordCountMsg || editor.getLang('wordCountMsg'),
            wordOverFlowMsg: editor.options.wordOverFlowMsg || editor.getLang('wordOverFlowMsg')
          })
          var opt = editor.options
          var max = opt.maximumWords
          var msg = opt.wordCountMsg
          var errMsg = opt.wordOverFlowMsg
          var countDom = ui.getDom('wordcount')
          if (!opt.wordCount) {
            return
          }
          var count = editor.getContentLength(true)
          if (count > max) {
            countDom.innerHTML = errMsg
            editor.fireEvent('wordcountoverflow')
          } else {
            countDom.innerHTML = msg.replace('{#leave}', max - count).replace('{#count}', count)
          }
        }

        editor.addListener('selectionchange', function() {
          if (editor.options.elementPathEnabled) {
            me[(editor.queryCommandState('elementpath') == -1 ? 'dis' : 'en') + 'ableElementPath']()
          }
          if (editor.options.scaleEnabled) {
            me[(editor.queryCommandState('scale') == -1 ? 'dis' : 'en') + 'ableScale']()
          }
        })
        var popup = new baidu.editor.ui.Popup({
          editor: editor,
          content: '',
          className: 'edui-bubble',
          _onEditButtonClick: function() {
            this.hide()
            editor.ui._dialogs.linkDialog.open()
          },
          _onImgEditButtonClick: function(name) {
            this.hide()
            editor.ui._dialogs[name] && editor.ui._dialogs[name].open()
          },
          _onImgSetFloat: function(value) {
            this.hide()
            editor.execCommand('imagefloat', value)
          },
          _setIframeAlign: function(value) {
            var frame = popup.anchorEl
            var newFrame = frame.cloneNode(true)
            switch (value) {
              case -2:
                newFrame.setAttribute('align', '')
                break
              case -1:
                newFrame.setAttribute('align', 'left')
                break
              case 1:
                newFrame.setAttribute('align', 'right')
                break
            }
            frame.parentNode.insertBefore(newFrame, frame)
            domUtils.remove(frame)
            popup.anchorEl = newFrame
            popup.showAnchor(popup.anchorEl)
          },
          _updateIframe: function() {
            var frame = (editor._iframe = popup.anchorEl)
            if (domUtils.hasClass(frame, 'ueditor_baidumap')) {
              editor.selection
                .getRange()
                .selectNode(frame)
                .select()
              editor.ui._dialogs.mapDialog.open()
              popup.hide()
            } else {
              editor.ui._dialogs.insertframeDialog.open()
              popup.hide()
            }
          },
          _onRemoveButtonClick: function(cmdName) {
            editor.execCommand(cmdName)
            this.hide()
          },
          queryAutoHide: function(el) {
            if (el && el.ownerDocument == editor.document) {
              if (el.tagName.toLowerCase() == 'img' || domUtils.findParentByTagName(el, 'a', true)) {
                return el !== popup.anchorEl
              }
            }
            return baidu.editor.ui.Popup.prototype.queryAutoHide.call(this, el)
          }
        })
        popup.render()
        if (editor.options.imagePopup) {
          editor.addListener('mouseover', function(t, evt) {
            evt = evt || window.event
            console.log('mouseover', evt)
            var el = evt.target || evt.srcElement
            if (editor.ui._dialogs.insertframeDialog && /iframe/gi.test(el.tagName)) {
              var html = popup.formatHtml(
                '<nobr>' +
                editor.getLang('property') +
                ': <span onclick=$$._setIframeAlign(-2) class="edui-clickable">' +
                editor.getLang('default') +
                '</span>&nbsp;&nbsp;<span onclick=$$._setIframeAlign(-1) class="edui-clickable">' +
                editor.getLang('justifyleft') +
                '</span>&nbsp;&nbsp;<span onclick=$$._setIframeAlign(1) class="edui-clickable">' +
                editor.getLang('justifyright') +
                '</span>&nbsp;&nbsp;' +
                ' <span onclick="$$._updateIframe( this);" class="edui-clickable">' +
                editor.getLang('modify') +
                '</span></nobr>'
              )
              if (html) {
                popup.getDom('content').innerHTML = html
                popup.anchorEl = el
                popup.showAnchor(popup.anchorEl)
              } else {
                popup.hide()
              }
            }
          })
          editor.addListener('selectionchange', function(t, causeByUi) {
            if (!causeByUi) {
              return
            }
            var html = ''
            var str = ''
            var img = editor.selection.getRange().getClosedNode()
            var dialogs = editor.ui._dialogs
            // console.log('selectionchange', img)
            // console.log('selectionchange', causeByUi)
            if (img && img.tagName == 'IMG') {
              var dialogName = 'insertimageDialog'

              if (img.className.indexOf('edui-faked-video') != -1 || img.className.indexOf('edui-upload-video') != -1) {
                dialogName = 'insertvideoDialog'
              }
              if (img.className.indexOf('edui-faked-webapp') != -1) {
                dialogName = 'webappDialog'
              }
              if (img.src.indexOf('http://api.map.baidu.com') != -1) {
                dialogName = 'mapDialog'
              }
              if (img.className.indexOf('edui-faked-music') != -1) {
                dialogName = 'musicDialog'
              }
              if (img.src.indexOf('http://maps.google.com/maps/api/staticmap') != -1) {
                dialogName = 'gmapDialog'
              }
              if (img.getAttribute('anchorname')) {
                dialogName = 'anchorDialog'
                html = popup.formatHtml(
                  '<nobr>' +
                  editor.getLang('property') +
                  ': <span onclick=$$._onImgEditButtonClick("anchorDialog") class="edui-clickable">' +
                  editor.getLang('modify') +
                  '</span>&nbsp;&nbsp;' +
                  '<span onclick=$$._onRemoveButtonClick(\'anchor\') class="edui-clickable">' +
                  editor.getLang('delete') +
                  '</span></nobr>'
                )
              }
              if (img.getAttribute('word_img')) {
                editor.word_img = [img.getAttribute('word_img')]
                dialogName = 'wordimageDialog'
              }
              if (domUtils.hasClass(img, 'loadingclass') || domUtils.hasClass(img, 'loaderrorclass')) {
                dialogName = ''
              }
              if (!dialogs[dialogName]) {
                return
              }
              str =
                '<nobr>' +
                editor.getLang('property') +
                ': ' +
                '<span onclick=$$._onImgSetFloat("none") class="edui-clickable">' +
                editor.getLang('default') +
                '</span>&nbsp;&nbsp;' +
                '<span onclick=$$._onImgSetFloat("left") class="edui-clickable">' +
                editor.getLang('justifyleft') +
                '</span>&nbsp;&nbsp;' +
                '<span onclick=$$._onImgSetFloat("right") class="edui-clickable">' +
                editor.getLang('justifyright') +
                '</span>&nbsp;&nbsp;' +
                '<span onclick=$$._onImgSetFloat("center") class="edui-clickable">' +
                editor.getLang('justifycenter') +
                '</span>&nbsp;&nbsp;' +
                '<span onclick="$$._onImgEditButtonClick(\'' +
                dialogName +
                '\');" class="edui-clickable">' +
                editor.getLang('modify') +
                '</span></nobr>'
              !html && (html = popup.formatHtml(str))
            }
            if (editor.ui._dialogs.linkDialog) {
              var link = editor.queryCommandValue('link')
              var url
              if (link && (url = link.getAttribute('_href') || link.getAttribute('href', 2))) {
                var txt = url
                if (url.length > 30) {
                  txt = url.substring(0, 20) + '...'
                }
                if (html) {
                  html += '<div style="height:5px;"></div>'
                }
                html += popup.formatHtml(
                  '<nobr>' +
                  editor.getLang('anthorMsg') +
                  ': <a target="_blank" href="' +
                  url +
                  '" title="' +
                  url +
                  '" >' +
                  txt +
                  '</a>' +
                  ' <span class="edui-clickable" onclick="$$._onEditButtonClick();">' +
                  editor.getLang('modify') +
                  '</span>' +
                  ' <span class="edui-clickable" onclick="$$._onRemoveButtonClick(\'unlink\');"> ' +
                  editor.getLang('clear') +
                  '</span></nobr>'
                )
                popup.showAnchor(link)
              }
            }
            if (html) {
              popup.getDom('content').innerHTML = html
              popup.anchorEl = img || link
              popup.showAnchor(popup.anchorEl)
            } else {
              popup.hide()
            }
          })
        }
      },
      _initToolbars: function() {
        var editor = this.editor
        var toolbars = this.toolbars || []
        var toolbarUis = []
        for (var i = 0; i < toolbars.length; i++) {
          var toolbar = toolbars[i]
          var toolbarUi = new baidu.editor.ui.Toolbar({ theme: editor.options.theme })
          for (var j = 0; j < toolbar.length; j++) {
            var toolbarItem = toolbar[j]
            var toolbarItemUi = null
            if (typeof toolbarItem === 'string') {
              toolbarItem = toolbarItem.toLowerCase()
              if (toolbarItem == '|') {
                toolbarItem = 'Separator'
              }
              if (toolbarItem == '||') {
                toolbarItem = 'Breakline'
              }
              if (baidu.editor.ui[toolbarItem]) {
                toolbarItemUi = new baidu.editor.ui[toolbarItem](editor)
              }
              if (toolbarItem == 'fullscreen') {
                if (toolbarUis && toolbarUis[0]) {
                  toolbarUis[0].items.splice(0, 0, toolbarItemUi)
                } else {
                  toolbarItemUi && toolbarUi.items.splice(0, 0, toolbarItemUi)
                }
                continue
              }
            } else {
              toolbarItemUi = toolbarItem
            }
            if (toolbarItemUi && toolbarItemUi.id) {
              toolbarUi.add(toolbarItemUi)
            }
          }
          toolbarUis[i] = toolbarUi
        }
        utils.each(UE._customizeUI, function(obj, key) {
          var itemUI, index
          if (obj.id && obj.id != editor.key) {
            return false
          }
          itemUI = obj.execFn.call(editor, editor, key)
          if (itemUI) {
            index = obj.index
            if (index === undefined) {
              index = toolbarUi.items.length
            }
            toolbarUi.add(itemUI, index)
          }
        })
        this.toolbars = toolbarUis
      },
      getHtmlTpl: function() {
        return (
          '<div id="##" class="%%">' +
          '<div id="##_toolbarbox" class="%%-toolbarbox">' +
          (this.toolbars.length
            ? '<div id="##_toolbarboxouter" class="%%-toolbarboxouter"><div class="%%-toolbarboxinner">' +
            this.renderToolbarBoxHtml() +
            '</div></div>'
            : '') +
          '<div id="##_toolbarmsg" class="%%-toolbarmsg" style="display:none;">' +
          '<div id = "##_upload_dialog" class="%%-toolbarmsg-upload" onclick="$$.showWordImageDialog();">' +
          this.editor.getLang('clickToUpload') +
          '</div>' +
          '<div class="%%-toolbarmsg-close" onclick="$$.hideToolbarMsg();">x</div>' +
          '<div id="##_toolbarmsg_label" class="%%-toolbarmsg-label"></div>' +
          '<div style="height:0;overflow:hidden;clear:both;"></div>' +
          '</div>' +
          '<div id="##_message_holder" class="%%-messageholder"></div>' +
          '</div>' +
          '<div id="##_iframeholder" class="%%-iframeholder">' +
          '</div>' +
          '<div id="##_bottombar" class="%%-bottomContainer"><table><tr>' +
          '<td id="##_elementpath" class="%%-bottombar"></td>' +
          '<td id="##_wordcount" class="%%-wordcount"></td>' +
          '<td id="##_scale" class="%%-scale"><div class="%%-icon"></div></td>' +
          '</tr></table></div>' +
          '<div id="##_scalelayer"></div>' +
          '</div>'
        )
      },
      showWordImageDialog: function() {
        this._dialogs['wordimageDialog'].open()
      },
      renderToolbarBoxHtml: function() {
        var buff = []
        for (var i = 0; i < this.toolbars.length; i++) {
          buff.push(this.toolbars[i].renderHtml())
        }
        return buff.join('')
      },
      setFullScreen: function(fullscreen) {
        var editor = this.editor
        var container = editor.container.parentNode.parentNode
        if (this._fullscreen != fullscreen) {
          this._fullscreen = fullscreen
          this.editor.fireEvent('beforefullscreenchange', fullscreen)
          if (baidu.editor.browser.gecko) {
            var bk = editor.selection.getRange().createBookmark()
          }
          if (fullscreen) {
            while (container.tagName != 'BODY') {
              var position = baidu.editor.dom.domUtils.getComputedStyle(container, 'position')
              nodeStack.push(position)
              container.style.position = 'static'
              container = container.parentNode
            }
            this._bakHtmlOverflow = document.documentElement.style.overflow
            this._bakBodyOverflow = document.body.style.overflow
            this._bakAutoHeight = this.editor.autoHeightEnabled
            this._bakScrollTop = Math.max(document.documentElement.scrollTop, document.body.scrollTop)
            this._bakEditorContaninerWidth = editor.iframe.parentNode.offsetWidth
            if (this._bakAutoHeight) {
              editor.autoHeightEnabled = false
              this.editor.disableAutoHeight()
            }
            document.documentElement.style.overflow = 'hidden'
            window.scrollTo(0, window.scrollY)
            this._bakCssText = this.getDom().style.cssText
            this._bakCssText1 = this.getDom('iframeholder').style.cssText
            editor.iframe.parentNode.style.width = ''
            this._updateFullScreen()
          } else {
            while (container.tagName != 'BODY') {
              container.style.position = nodeStack.shift()
              container = container.parentNode
            }
            this.getDom().style.cssText = this._bakCssText
            this.getDom('iframeholder').style.cssText = this._bakCssText1
            if (this._bakAutoHeight) {
              editor.autoHeightEnabled = true
              this.editor.enableAutoHeight()
            }
            document.documentElement.style.overflow = this._bakHtmlOverflow
            document.body.style.overflow = this._bakBodyOverflow
            editor.iframe.parentNode.style.width = this._bakEditorContaninerWidth + 'px'
            window.scrollTo(0, this._bakScrollTop)
          }
          if (browser.gecko && editor.body.contentEditable === 'true') {
            var input = document.createElement('input')
            document.body.appendChild(input)
            editor.body.contentEditable = false
            setTimeout(function() {
              input.focus()
              setTimeout(function() {
                editor.body.contentEditable = true
                editor.fireEvent('fullscreenchanged', fullscreen)
                editor.selection
                  .getRange()
                  .moveToBookmark(bk)
                  .select(true)
                baidu.editor.dom.domUtils.remove(input)
                fullscreen && window.scroll(0, 0)
              }, 0)
            }, 0)
          }
          if (editor.body.contentEditable === 'true') {
            this.editor.fireEvent('fullscreenchanged', fullscreen)
            this.triggerLayout()
          }
        }
      },
      _updateFullScreen: function() {
        if (this._fullscreen) {
          var vpRect = uiUtils.getViewportRect()
          this.getDom().style.cssText =
            'border:0;position:absolute;left:0;top:' +
            (this.editor.options.topOffset || 0) +
            'px;width:' +
            vpRect.width +
            'px;height:' +
            vpRect.height +
            'px;z-index:' +
            (this.getDom().style.zIndex * 1 + 100)
          uiUtils.setViewportOffset(this.getDom(), { left: 0, top: this.editor.options.topOffset || 0 })
          this.editor.setHeight(
            vpRect.height -
            this.getDom('toolbarbox').offsetHeight -
            this.getDom('bottombar').offsetHeight -
            (this.editor.options.topOffset || 0),
            true
          )
          if (browser.gecko) {
            try {
              window.onresize()
            } catch (e) {
            }
          }
        }
      },
      _updateElementPath: function() {
        var bottom = this.getDom('elementpath')
        var list
        if (this.elementPathEnabled && (list = this.editor.queryCommandValue('elementpath'))) {
          var buff = []
          for (var i = 0, ci; (ci = list[i]); i++) {
            buff[i] = this.formatHtml(
              '<span unselectable="on" onclick="$$.editor.execCommand(&quot;elementpath&quot;, &quot;' +
              i +
              '&quot;);">' +
              ci +
              '</span>'
            )
          }
          bottom.innerHTML =
            '<div class="edui-editor-breadcrumb" onmousedown="return false;">' +
            this.editor.getLang('elementPathTip') +
            ': ' +
            buff.join(' &gt; ') +
            '</div>'
        } else {
          bottom.style.display = 'none'
        }
      },
      disableElementPath: function() {
        var bottom = this.getDom('elementpath')
        bottom.innerHTML = ''
        bottom.style.display = 'none'
        this.elementPathEnabled = false
      },
      enableElementPath: function() {
        var bottom = this.getDom('elementpath')
        bottom.style.display = ''
        this.elementPathEnabled = true
        this._updateElementPath()
      },
      _scale: function() {
        var doc = document
        var editor = this.editor
        var editorHolder = editor.container
        var editorDocument = editor.document
        var toolbarBox = this.getDom('toolbarbox')
        var bottombar = this.getDom('bottombar')
        var scale = this.getDom('scale')
        var scalelayer = this.getDom('scalelayer')
        var isMouseMove = false
        var position = null
        var minEditorHeight = 0
        var minEditorWidth = editor.options.minFrameWidth
        var pageX = 0
        var pageY = 0
        var scaleWidth = 0
        var scaleHeight = 0

        function down() {
          position = domUtils.getXY(editorHolder)
          if (!minEditorHeight) {
            minEditorHeight = editor.options.minFrameHeight + toolbarBox.offsetHeight + bottombar.offsetHeight
          }
          scalelayer.style.cssText =
            'position:absolute;left:0;display:;top:0;background-color:#41ABFF;opacity:0.4;filter: Alpha(opacity=40);width:' +
            editorHolder.offsetWidth +
            'px;height:' +
            editorHolder.offsetHeight +
            'px;z-index:' +
            (editor.options.zIndex + 1)
          domUtils.on(doc, 'mousemove', move)
          domUtils.on(editorDocument, 'mouseup', up)
          domUtils.on(doc, 'mouseup', up)
        }

        var me = this
        this.editor.addListener('fullscreenchanged', function(e, fullScreen) {
          if (fullScreen) {
            me.disableScale()
          } else {
            if (me.editor.options.scaleEnabled) {
              me.enableScale()
              var tmpNode = me.editor.document.createElement('span')
              me.editor.body.appendChild(tmpNode)
              me.editor.body.style.height =
                Math.max(domUtils.getXY(tmpNode).y, me.editor.iframe.offsetHeight - 20) + 'px'
              domUtils.remove(tmpNode)
            }
          }
        })

        function move(event) {
          clearSelection()
          var e = event || window.event
          pageX = e.pageX || doc.documentElement.scrollLeft + e.clientX
          pageY = e.pageY || doc.documentElement.scrollTop + e.clientY
          scaleWidth = pageX - position.x
          scaleHeight = pageY - position.y
          if (scaleWidth >= minEditorWidth) {
            isMouseMove = true
            scalelayer.style.width = scaleWidth + 'px'
          }
          if (scaleHeight >= minEditorHeight) {
            isMouseMove = true
            scalelayer.style.height = scaleHeight + 'px'
          }
        }

        function up() {
          if (isMouseMove) {
            isMouseMove = false
            editor.ui._actualFrameWidth = scalelayer.offsetWidth - 2
            editorHolder.style.width = editor.ui._actualFrameWidth + 'px'
            editor.setHeight(scalelayer.offsetHeight - bottombar.offsetHeight - toolbarBox.offsetHeight - 2, true)
          }
          if (scalelayer) {
            scalelayer.style.display = 'none'
          }
          clearSelection()
          domUtils.un(doc, 'mousemove', move)
          domUtils.un(editorDocument, 'mouseup', up)
          domUtils.un(doc, 'mouseup', up)
        }

        function clearSelection() {
          if (browser.ie) {
            doc.selection.clear()
          } else {
            window.getSelection().removeAllRanges()
          }
        }

        this.enableScale = function() {
          if (editor.queryCommandState('source') == 1) {
            return
          }
          scale.style.display = ''
          this.scaleEnabled = true
          domUtils.on(scale, 'mousedown', down)
        }
        this.disableScale = function() {
          scale.style.display = 'none'
          this.scaleEnabled = false
          domUtils.un(scale, 'mousedown', down)
        }
      },
      isFullScreen: function() {
        return this._fullscreen
      },
      postRender: function() {
        UIBase.prototype.postRender.call(this)
        for (var i = 0; i < this.toolbars.length; i++) {
          this.toolbars[i].postRender()
        }
        var me = this
        var timerId
        var domUtils = baidu.editor.dom.domUtils
        var updateFullScreenTime = function() {
          clearTimeout(timerId)
          timerId = setTimeout(function() {
            me._updateFullScreen()
          })
        }
        domUtils.on(window, 'resize', updateFullScreenTime)
        me.addListener('destroy', function() {
          domUtils.un(window, 'resize', updateFullScreenTime)
          clearTimeout(timerId)
        })
      },
      showToolbarMsg: function(msg, flag) {
        this.getDom('toolbarmsg_label').innerHTML = msg
        this.getDom('toolbarmsg').style.display = ''
        if (!flag) {
          var w = this.getDom('upload_dialog')
          w.style.display = 'none'
        }
      },
      hideToolbarMsg: function() {
        this.getDom('toolbarmsg').style.display = 'none'
      },
      mapUrl: function(url) {
        return url ? url.replace('~/', this.editor.options.UEDITOR_HOME_URL || '') : ''
      },
      triggerLayout: function() {
        var dom = this.getDom()
        if (dom.style.zoom == '1') {
          dom.style.zoom = '100%'
        } else {
          dom.style.zoom = '1'
        }
      }
    }
    utils.inherits(EditorUI, baidu.editor.ui.UIBase)
    var instances = {}
    UE.ui.Editor = function(options) {
      var editor = new UE.Editor(options)
      editor.options.editor = editor
      utils.loadFile(document, {
        href: editor.options.themePath + editor.options.theme + '/css/ueditor.css',
        tag: 'link',
        type: 'text/css',
        rel: 'stylesheet'
      })
      var oldRender = editor.render
      editor.render = function(holder) {
        if (holder.constructor === String) {
          editor.key = holder
          instances[holder] = editor
        }
        utils.domReady(function() {
          editor.langIsReady ? renderUI() : editor.addListener('langReady', renderUI)

          function renderUI() {
            editor.setOpt({ labelMap: editor.options.labelMap || editor.getLang('labelMap') })
            new EditorUI(editor.options)
            if (holder) {
              if (holder.constructor === String) {
                holder = document.getElementById(holder)
              }
              holder && holder.getAttribute('name') && (editor.options.textarea = holder.getAttribute('name'))
              if (holder && /script|textarea/gi.test(holder.tagName)) {
                var newDiv = document.createElement('div')
                holder.parentNode.insertBefore(newDiv, holder)
                var cont = holder.value || holder.innerHTML
                editor.options.initialContent = /^[\t\r\n ]*$/.test(cont)
                  ? editor.options.initialContent
                  : cont
                    .replace(/>[\n\r\t]+([ ]{4})+/g, '>')
                    .replace(/[\n\r\t]+([ ]{4})+</g, '<')
                    .replace(/>[\n\r\t]+</g, '><')
                holder.className && (newDiv.className = holder.className)
                holder.style.cssText && (newDiv.style.cssText = holder.style.cssText)
                if (/textarea/i.test(holder.tagName)) {
                  editor.textarea = holder
                  editor.textarea.style.display = 'none'
                } else {
                  holder.parentNode.removeChild(holder)
                }
                if (holder.id) {
                  newDiv.id = holder.id
                  domUtils.removeAttributes(holder, 'id')
                }
                holder = newDiv
                holder.innerHTML = ''
              }
            }
            domUtils.addClass(holder, 'edui-' + editor.options.theme)
            editor.ui.render(holder)
            var opt = editor.options
            editor.container = editor.ui.getDom()
            var parents = domUtils.findParents(holder, true)
            var displays = []
            for (var i = 0, ci; (ci = parents[i]); i++) {
              displays[i] = ci.style.display
              ci.style.display = 'block'
            }
            if (opt.initialFrameWidth) {
              opt.minFrameWidth = opt.initialFrameWidth
            } else {
              opt.minFrameWidth = opt.initialFrameWidth = holder.offsetWidth
              var styleWidth = holder.style.width
              if (/%$/.test(styleWidth)) {
                opt.initialFrameWidth = styleWidth
              }
            }
            if (opt.initialFrameHeight) {
              opt.minFrameHeight = opt.initialFrameHeight
            } else {
              opt.initialFrameHeight = opt.minFrameHeight = holder.offsetHeight
            }
            for (var i = 0, ci; (ci = parents[i]); i++) {
              ci.style.display = displays[i]
            }
            if (holder.style.height) {
              holder.style.height = ''
            }
            editor.container.style.width = opt.initialFrameWidth + (/%$/.test(opt.initialFrameWidth) ? '' : 'px')
            editor.container.style.zIndex = opt.zIndex
            oldRender.call(editor, editor.ui.getDom('iframeholder'))
            editor.fireEvent('afteruiready')
          }
        })
      }
      return editor
    }
    UE.getEditor = function(id, opt) {
      var editor = instances[id]
      if (!editor) {
        editor = instances[id] = new UE.ui.Editor(opt)
        editor.render(id)
      }
      return editor
    }
    UE.delEditor = function(id) {
      var editor
      if ((editor = instances[id])) {
        editor.key && editor.destroy()
        delete instances[id]
      }
    }
    UE.registerUI = function(uiName, fn, index, editorId) {
      utils.each(uiName.split(/\s+/), function(name) {
        UE._customizeUI[name] = { id: editorId, execFn: fn, index: index }
      })
    }
  })()
  UE.registerUI('message', function(editor) {
    var editorui = baidu.editor.ui
    var Message = editorui.Message
    var holder
    var _messageItems = []
    var me = editor
    me.addListener('ready', function() {
      holder = document.getElementById(me.ui.id + '_message_holder')
      updateHolderPos()
      setTimeout(function() {
        updateHolderPos()
      }, 500)
    })
    me.addListener('showmessage', function(type, opt) {
      opt = utils.isString(opt) ? { content: opt } : opt
      var message = new Message({
        timeout: opt.timeout,
        type: opt.type,
        content: opt.content,
        keepshow: opt.keepshow,
        editor: me
      })
      var mid = opt.id || 'msg_' + (+new Date()).toString(36)
      message.render(holder)
      _messageItems[mid] = message
      message.reset(opt)
      updateHolderPos()
      return mid
    })
    me.addListener('updatemessage', function(type, id, opt) {
      opt = utils.isString(opt) ? { content: opt } : opt
      var message = _messageItems[id]
      message.render(holder)
      message && message.reset(opt)
    })
    me.addListener('hidemessage', function(type, id) {
      var message = _messageItems[id]
      message && message.hide()
    })

    function updateHolderPos() {
      var toolbarbox = me.ui.getDom('toolbarbox')
      if (toolbarbox) {
        holder.style.top = toolbarbox.offsetHeight + 3 + 'px'
      }
      holder.style.zIndex = Math.max(me.options.zIndex, me.iframe.style.zIndex) + 1
    }
  })
  UE.registerUI('autosave', function(editor) {
    var timer = null
    var uid = null
    editor.on('afterautosave', function() {
      clearTimeout(timer)
      timer = setTimeout(function() {
        if (uid) {
          editor.trigger('hidemessage', uid)
        }
        uid = editor.trigger('showmessage', { content: editor.getLang('autosave.success'), timeout: 2000 })
      }, 2000)
    })
  })
})()
